diff options
author | Alon Zakai <alonzakai@gmail.com> | 2013-10-08 11:47:14 -0400 |
---|---|---|
committer | Alon Zakai <alonzakai@gmail.com> | 2013-10-08 11:47:14 -0400 |
commit | 0a229cf5a240a1ebf4ff5d0ebb501a286bc96202 (patch) | |
tree | 2797d054788e88863d77f9826dde94fb4c276c69 /src | |
parent | 05b6aa32a5f1633797f7eae390b3a8048b29ca69 (diff) | |
parent | ae5ef852920ce67449af7693f05a767a87aed976 (diff) |
Merge branch 'incoming'
Diffstat (limited to 'src')
-rw-r--r-- | src/analyzer.js | 34 | ||||
-rw-r--r-- | src/compiler.js | 15 | ||||
-rw-r--r-- | src/compiler_phase.html | 33 | ||||
-rw-r--r-- | src/fastLong.js | 12 | ||||
-rw-r--r-- | src/intertyper.js | 168 | ||||
-rw-r--r-- | src/jsifier.js | 216 | ||||
-rw-r--r-- | src/library.js | 845 | ||||
-rw-r--r-- | src/library_fs.js | 83 | ||||
-rw-r--r-- | src/library_gl.js | 168 | ||||
-rw-r--r-- | src/library_idbfs.js | 216 | ||||
-rw-r--r-- | src/library_memfs.js | 23 | ||||
-rw-r--r-- | src/library_nodefs.js | 234 | ||||
-rw-r--r-- | src/library_sdl.js | 465 | ||||
-rw-r--r-- | src/library_sockfs.js | 12 | ||||
-rw-r--r-- | src/modules.js | 28 | ||||
-rw-r--r-- | src/parseTools.js | 314 | ||||
-rw-r--r-- | src/preamble.js | 31 | ||||
-rw-r--r-- | src/runtime.js | 19 | ||||
-rw-r--r-- | src/settings.js | 444 | ||||
-rw-r--r-- | src/struct_info.json | 1045 | ||||
-rw-r--r-- | src/utility.js | 11 |
21 files changed, 3029 insertions, 1387 deletions
diff --git a/src/analyzer.js b/src/analyzer.js index 750f2a4c..17ad26ad 100644 --- a/src/analyzer.js +++ b/src/analyzer.js @@ -30,6 +30,8 @@ function analyzer(data, sidePass) { var item = { items: data }; var data = item; + var newTypes = {}; + // Gather // Single-liners ['globalVariable', 'functionStub', 'unparsedFunction', 'unparsedGlobals', 'unparsedTypes', 'alias'].forEach(function(intertype) { @@ -42,6 +44,7 @@ function analyzer(data, sidePass) { temp.splitOut.forEach(function(type) { //dprint('types', 'adding defined type: ' + type.name_); Types.types[type.name_] = type; + newTypes[type.name_] = 1; if (QUANTUM_SIZE === 1) { Types.fatTypes[type.name_] = copy(type); } @@ -780,13 +783,14 @@ function analyzer(data, sidePass) { assert(PRECISE_I64_MATH, 'Must have precise i64 math for non-constant 64-bit shifts'); Types.preciseI64MathUsed = 1; value.intertype = 'value'; - value.ident = 'var ' + value.assignTo + '$0 = ' + + value.ident = makeVarDef(value.assignTo) + '$0=' + asmCoercion('_bitshift64' + value.op[0].toUpperCase() + value.op.substr(1) + '(' + asmCoercion(sourceElements[0].ident, 'i32') + ',' + asmCoercion(sourceElements[1].ident, 'i32') + ',' + asmCoercion(value.params[1].ident + '$0', 'i32') + ')', 'i32' ) + ';' + - 'var ' + value.assignTo + '$1 = tempRet0;'; + makeVarDef(value.assignTo) + '$1=tempRet0;'; + value.vars = [[value.assignTo + '$0', 'i32'], [value.assignTo + '$1', 'i32']]; value.assignTo = null; i++; continue; @@ -897,7 +901,7 @@ function analyzer(data, sidePass) { }); } - function addTypeInternal(type, data) { + function addTypeInternal(type) { if (type.length == 1) return; if (Types.types[type]) return; if (['internal', 'hidden', 'inbounds', 'void'].indexOf(type) != -1) return; @@ -908,8 +912,9 @@ function analyzer(data, sidePass) { // to look at the underlying type - it was not defined explicitly // anywhere else. var nonPointing = removeAllPointing(type); + if (Types.types[nonPointing]) return; var check = /^\[(\d+)\ x\ (.*)\]$/.exec(nonPointing); - if (check && !Types.types[nonPointing]) { + if (check) { var num = parseInt(check[1]); num = Math.max(num, 1); // [0 x something] is used not for allocations and such of course, but // for indexing - for an |array of unknown length|, basically. So we @@ -917,7 +922,7 @@ function analyzer(data, sidePass) { // check that we never allocate with this (either as a child structure // in the analyzer, or in calcSize in alloca). var subType = check[2]; - addTypeInternal(subType, data); // needed for anonymous structure definitions (see below) + addTypeInternal(subType); // needed for anonymous structure definitions (see below) // Huge structural types are represented very inefficiently, both here and in generated JS. Best to avoid them - for example static char x[10*1024*1024]; is bad, while static char *x = malloc(10*1024*1024) is fine. if (num >= 10*1024*1024) warnOnce('warning: very large fixed-size structural type: ' + type + ' - can you reduce it? (compilation may be slow)'); @@ -926,6 +931,7 @@ function analyzer(data, sidePass) { fields: range(num).map(function() { return subType }), lineNum: '?' }; + newTypes[nonPointing] = 1; // Also add a |[0 x type]| type var zerod = '[0 x ' + subType + ']'; if (!Types.types[zerod]) { @@ -934,6 +940,7 @@ function analyzer(data, sidePass) { fields: [subType, subType], // Two, so we get the flatFactor right. We care about the flatFactor, not the size here lineNum: '?' }; + newTypes[zerod] = 1; } return; } @@ -964,6 +971,7 @@ function analyzer(data, sidePass) { packed: packed, lineNum: '?' }; + newTypes[type] = 1; return; } @@ -975,13 +983,14 @@ function analyzer(data, sidePass) { flatSize: 1, lineNum: '?' }; + newTypes[type] = 1; } - function addType(type, data) { - addTypeInternal(type, data); + function addType(type) { + addTypeInternal(type); if (QUANTUM_SIZE === 1) { Types.flipTypes(); - addTypeInternal(type, data); + addTypeInternal(type); Types.flipTypes(); } } @@ -992,7 +1001,7 @@ function analyzer(data, sidePass) { // which handles type definitions, and later. Doing so before the first side pass will result in // making bad guesses about types which are actually defined for (var type in Types.needAnalysis) { - if (type) addType(type, data); + if (type) addType(type); } Types.needAnalysis = {}; } @@ -1021,17 +1030,18 @@ function analyzer(data, sidePass) { var more = true; while (more) { more = false; - for (var typeName in types) { + for (var typeName in newTypes) { var type = types[typeName]; if (type.flatIndexes) continue; var ready = true; type.fields.forEach(function(field) { if (isStructType(field)) { if (!types[field]) { - addType(field, item); + addType(field); ready = false; } else { if (!types[field].flatIndexes) { + newTypes[field] = 1; ready = false; } } @@ -1058,6 +1068,8 @@ function analyzer(data, sidePass) { Runtime.QUANTUM_SIZE = trueQuantumSize; Types.flipTypes(); } + + newTypes = null; } // Variable analyzer diff --git a/src/compiler.js b/src/compiler.js index 4f16986c..90060837 100644 --- a/src/compiler.js +++ b/src/compiler.js @@ -206,6 +206,11 @@ if (phase == 'pre') { if (VERBOSE) printErr('VERBOSE is on, this generates a lot of output and can slow down compilation'); +// Load struct and define information. +var temp = JSON.parse(read(STRUCT_INFO)); +C_STRUCTS = temp.structs; +C_DEFINES = temp.defines; + // Load compiler code load('modules.js'); @@ -215,7 +220,12 @@ load('analyzer.js'); load('jsifier.js'); if (phase == 'funcs' && RELOOP) { // XXX handle !singlePhase RelooperModule = { TOTAL_MEMORY: ceilPowerOfTwo(2*RELOOPER_BUFFER_SIZE) }; - load(RELOOPER); + try { + load(RELOOPER); + } catch(e) { + printErr('cannot load relooper at ' + RELOOPER + ' : ' + e + ', trying in current dir'); + load('relooper.js'); + } assert(typeof Relooper != 'undefined'); } globalEval(processMacros(preprocess(read('runtime.js')))); @@ -269,6 +279,9 @@ function compile(raw) { intertyped = null; JSify(analyzed); + //dumpInterProf(); + //printErr(phase + ' paths (fast, slow): ' + [fastPaths, slowPaths]); + phase = null; if (DEBUG_MEMORY) { diff --git a/src/compiler_phase.html b/src/compiler_phase.html new file mode 100644 index 00000000..8ca631e8 --- /dev/null +++ b/src/compiler_phase.html @@ -0,0 +1,33 @@ +<html> +<body> +<h2>Run the emscripten compiler in a web page, just for laughs</h2> +Open the web console to see stderr output +<hr> +<pre id="output"></pre> +<script> + arguments = ['tmp/emscripten_temp/tmpbTF9CI.txt', 'tmp/emscripten_temp/tmpz8Yvie.pre.ll', 'pre']; // copy from emscripten.py output + + var outputElement = document.getElementById('output'); + print = function(x) { + outputElement.innerHTML += 'output hidden, profiling mode'; + print = function(){}; + //outputElement.innerHTML += x; + }; + + // For generated code + var Module = { + print: function(x) { + throw 'what?' + } + }; + + var startTime = Date.now(); +</script> +<script src="compiler.js"> +</script> +<script> + outputElement.innerHTML += '<br>total time: ' + (Date.now() - startTime); +</script> +</body> +</html> + diff --git a/src/fastLong.js b/src/fastLong.js index 4f6efd9f..2b70b2fb 100644 --- a/src/fastLong.js +++ b/src/fastLong.js @@ -5,12 +5,12 @@ function ___muldsi3($a, $b) { var $1 = 0, $2 = 0, $3 = 0, $6 = 0, $8 = 0, $11 = 0, $12 = 0; $1 = $a & 65535; $2 = $b & 65535; - $3 = Math.imul($2, $1) | 0; + $3 = Math_imul($2, $1) | 0; $6 = $a >>> 16; - $8 = ($3 >>> 16) + (Math.imul($2, $6) | 0) | 0; + $8 = ($3 >>> 16) + (Math_imul($2, $6) | 0) | 0; $11 = $b >>> 16; - $12 = Math.imul($11, $1) | 0; - return (tempRet0 = (($8 >>> 16) + (Math.imul($11, $6) | 0) | 0) + ((($8 & 65535) + $12 | 0) >>> 16) | 0, 0 | ($8 + $12 << 16 | $3 & 65535)) | 0; + $12 = Math_imul($11, $1) | 0; + return (tempRet0 = (($8 >>> 16) + (Math_imul($11, $6) | 0) | 0) + ((($8 & 65535) + $12 | 0) >>> 16) | 0, 0 | ($8 + $12 << 16 | $3 & 65535)) | 0; } function ___divdi3($a$0, $a$1, $b$0, $b$1) { $a$0 = $a$0 | 0; @@ -63,8 +63,8 @@ function ___muldi3($a$0, $a$1, $b$0, $b$1) { $y_sroa_0_0_extract_trunc = $b$0; $1$0 = ___muldsi3($x_sroa_0_0_extract_trunc, $y_sroa_0_0_extract_trunc) | 0; $1$1 = tempRet0; - $2 = Math.imul($a$1, $y_sroa_0_0_extract_trunc) | 0; - return (tempRet0 = ((Math.imul($b$1, $x_sroa_0_0_extract_trunc) | 0) + $2 | 0) + $1$1 | $1$1 & 0, 0 | $1$0 & -1) | 0; + $2 = Math_imul($a$1, $y_sroa_0_0_extract_trunc) | 0; + return (tempRet0 = ((Math_imul($b$1, $x_sroa_0_0_extract_trunc) | 0) + $2 | 0) + $1$1 | $1$1 & 0, 0 | $1$0 & -1) | 0; } function ___udivdi3($a$0, $a$1, $b$0, $b$1) { $a$0 = $a$0 | 0; diff --git a/src/intertyper.js b/src/intertyper.js index e43cc298..09bdaa33 100644 --- a/src/intertyper.js +++ b/src/intertyper.js @@ -3,6 +3,8 @@ // LLVM assembly => internal intermediate representation, which is ready // to be processed by the later stages. +var fastPaths = 0, slowPaths = 0; + // Line tokenizer function tokenizer(item, inner) { //assert(item.lineNum != 40000); @@ -373,6 +375,14 @@ function intertyper(lines, sidePass, baseLineNums) { // Line parsers to intermediate form // globals: type or variable + function noteGlobalVariable(ret) { + if (!NAMED_GLOBALS) { + Variables.globals[ret.ident].type = ret.type; + Variables.globals[ret.ident].external = ret.external; + } + Types.needAnalysis[ret.type] = 0; + } + function globalHandler(item) { function scanConst(value, type) { // Gets an array of constant items, separated by ',' tokens @@ -497,7 +507,6 @@ function intertyper(lines, sidePass, baseLineNums) { external = true; item.tokens.splice(2, 1); } - Types.needAnalysis[item.tokens[2].text] = 0; var ret = { intertype: 'globalVariable', ident: toNiceIdent(ident), @@ -507,11 +516,7 @@ function intertyper(lines, sidePass, baseLineNums) { named: named, lineNum: item.lineNum }; - if (!NAMED_GLOBALS) { - Variables.globals[ret.ident].type = ret.type; - Variables.globals[ret.ident].external = external; - } - Types.needAnalysis[ret.type] = 0; + noteGlobalVariable(ret); if (ident == '@llvm.global_ctors') { ret.ctors = []; if (item.tokens[3].item) { @@ -845,6 +850,7 @@ function intertyper(lines, sidePass, baseLineNums) { // TODO: also remove 2nd param? } else if (item.op in LLVM.COMPS) { item.type = 'i1'; + if (item.params[1].intertype === 'type') item.params[1].intertype = 'value'; // parsed as type, but comparisons have just values there } if (USE_TYPED_ARRAYS == 2) { // Some specific corrections, since 'i64' is special @@ -984,11 +990,150 @@ function intertyper(lines, sidePass, baseLineNums) { return ret; } + // Fast paths - quick parses of common patterns, avoid tokenizing entirely + + function tryFastPaths(line) { + var m, ret; + if (phase === 'pre') { + // string constant + if (0) { // works, but not worth it m = /([@\.\w\d_]+) = (private )?(unnamed_addr )?(constant )?(\[\d+ x i8\]) c"([^"]+)".*/.exec(line.lineText)) { + if (m[1] === '@llvm.global_ctors') return ret; + ret = { + intertype: 'globalVariable', + ident: toNiceIdent(m[1]), + type: m[5], + external: false, + private_: m[2] !== null, + named: m[3] === null, + lineNum: line.lineNum, + value: { + intertype: 'string', + text: m[6] + } + }; + noteGlobalVariable(ret); + } + } else if (phase === 'funcs') { + // TODO: (void)call, store + if (m = /^ (%[\w\d\._]+) = (getelementptr|load|icmp) ([%\w\d\._ ,\*\-@]+)$/.exec(line.lineText)) { + var assignTo = m[1]; + var intertype = m[2]; + var args = m[3]; + switch (intertype) { + case 'getelementptr': { + if (args[0] === 'i' && args.indexOf('inbounds ') === 0) { + args = args.substr(9); + } + var params = args.split(', ').map(function(param) { + var parts = param.split(' '); + assert(parts.length === 2); + Types.needAnalysis[parts[0]] = 0; + return { + intertype: 'value', + type: parts[0], + ident: toNiceIdent(parts[1]), + byVal: 0 + } + }); + ret = { + intertype: 'getelementptr', + lineNum: line.lineNum, + assignTo: toNiceIdent(assignTo), + ident: params[0].ident, + type: '*', + params: params + }; + break; + } + case 'load': { + if (m = /(^[%\w\d\._\-@\*]+) ([%\w\d\._\-@]+)(, align \d+)?$/.exec(args)) { + var ident = toNiceIdent(m[2]); + var type = m[1]; + assert(type[type.length-1] === '*', type); + var valueType = type.substr(0, type.length-1); + ret = { + intertype: 'load', + lineNum: line.lineNum, + assignTo: toNiceIdent(assignTo), + ident: ident, + type: valueType, + valueType: valueType, + pointerType: type, + pointer: { + intertype: 'value', + ident: ident, + type: type, + }, + align: parseAlign(m[3]) + }; + } + break; + } + case 'icmp': { + var parts = args.split(' '); + assert(parts.length === 4); + ret = { + intertype: 'mathop', + op: 'icmp', + variant: parts[0], + lineNum: line.lineNum, + assignTo: toNiceIdent(assignTo), + params: [{ + intertype: 'value', + ident: toNiceIdent(parts[2].substr(0, parts[2].length-1)), + type: parts[1] + }, { + intertype: 'value', + ident: toNiceIdent(parts[3]), + type: parts[1] + }], + type: 'i1', + }; + break; + } + default: throw 'unexpected fast path type ' + intertype; + } + } + //else if (line.lineText.indexOf(' = icmp ') > 0) printErr('close: ' + JSON.stringify(line.lineText)); + } + if (ret) { + if (COMPILER_ASSERTIONS) { + //printErr(['\n', dump(ret), '\n', dump(triager(tokenizer(line)))]); + var normal = triager(tokenizer(line)); + delete normal.tokens; + delete normal.indent; + assert(sortedJsonCompare(normal, ret), 'fast path: ' + dump(normal) + '\n vs \n' + dump(ret)); + } + } + return ret; + } + // Input lineSplitter().forEach(function(line) { + var item; + if (COMPILER_FASTPATHS) { + item = tryFastPaths(line); + if (item) { + finalResults.push(item); + fastPaths++; + return; + } + } + slowPaths++; + + //var time = Date.now(); + var t = tokenizer(line); - var item = triager(t); + item = triager(t); + + /* + var type = item ? item.intertype + (item.op ? ':' + item.op : ''): 'none'; + if (!interProf[type]) interProf[type] = { ms: 0, n: 0 }; + interProf[type].ms += Date.now() - time; + interProf[type].n++; + */ + if (!item) return; finalResults.push(item); if (item.tokens) item.tokens = null; // We do not need tokens, past the intertyper. Clean them up as soon as possible here. @@ -996,3 +1141,12 @@ function intertyper(lines, sidePass, baseLineNums) { return finalResults; } +// intertyper profiler + +/* +var interProf = {}; +function dumpInterProf() { + printErr('\nintertyper/' + phase + ' (ms | n): ' + JSON.stringify(keys(interProf).sort(function(x, y) { return interProf[y].ms - interProf[x].ms }).map(function(x) { return x + ' : ' + interProf[x].ms + ' | ' + interProf[x].n }), null, ' ') + '\n'); +} +*/ + diff --git a/src/jsifier.js b/src/jsifier.js index 96cb8d9a..0e5f8ef3 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -347,7 +347,7 @@ function JSify(data, functionsOnly, givenFunctions) { js += 'if (globalScope) { assert(!globalScope["' + item.ident + '"]); globalScope["' + item.ident + '"] = ' + item.ident + ' }'; } if (item.external && !NAMED_GLOBALS) { - js = 'var ' + item.ident + ' = ' + js; // force an explicit naming, even if unnamed globals, for asm forwarding + js = 'var ' + item.ident + '=' + js; // force an explicit naming, even if unnamed globals, for asm forwarding } itemsDict.GlobalVariableStub.push({ intertype: 'GlobalVariable', @@ -418,7 +418,7 @@ function JSify(data, functionsOnly, givenFunctions) { } // In asm, we need to know about library functions. If there is a target, though, then no // need to consider this a library function - we will call directly to it anyhow - if (ASM_JS && !redirectedIdent && (typeof target == 'function' || /Math\.\w+/.exec(snippet))) { + if (ASM_JS && !redirectedIdent && (typeof target == 'function' || /Math_\w+/.exec(snippet))) { Functions.libraryFunctions[ident] = 1; } } else if (typeof snippet === 'object') { @@ -537,6 +537,7 @@ function JSify(data, functionsOnly, givenFunctions) { default: throw 'what is this line? ' + dump(line); } assert(line.JS); + //if (ASM_JS) assert(line.JS.indexOf('var ') < 0, dump(line)); if (line.assignTo) makeAssign(line); Framework.currItem = null; }); @@ -584,7 +585,7 @@ function JSify(data, functionsOnly, givenFunctions) { if (DLOPEN_SUPPORT) Functions.getIndex(func.ident); - func.JS += 'function ' + func.ident + '(' + paramIdents.join(', ') + ') {\n'; + func.JS += 'function ' + func.ident + '(' + paramIdents.join(',') + '){\n'; if (PGO) { func.JS += INDENTATION + 'PGOMonitor.called["' + func.ident + '"] = 1;\n'; @@ -593,13 +594,23 @@ function JSify(data, functionsOnly, givenFunctions) { if (ASM_JS) { // spell out argument types func.params.forEach(function(param) { - func.JS += INDENTATION + param.ident + ' = ' + asmCoercion(param.ident, param.type) + ';\n'; + func.JS += INDENTATION + param.ident + '=' + deParen(asmCoercion(param.ident, param.type)) + ';\n'; }); + addVariable('label', 'i32', func); + + if (func.setjmpTable) { + addVariable('setjmpLabel', 'i32', func); + addVariable('setjmpTable', 'i32', func); + } + // spell out local variables - var vars = values(func.variables).filter(function(v) { return v.origin != 'funcparam' }); + var vars = values(func.variables).filter(function(v) { + return v.origin !== 'funcparam' && + (!isIllegalType(getImplementationType(v)) || v.ident.indexOf('$', 1) > 0); // not illegal, or a broken up illegal (we have illegal chunks explicitly anyhow) + }); if (vars.length > 0) { - var chunkSize = 8; + var chunkSize = 20; var chunks = []; var i = 0; while (i < vars.length) { @@ -608,22 +619,15 @@ function JSify(data, functionsOnly, givenFunctions) { } for (i = 0; i < chunks.length; i++) { func.JS += INDENTATION + 'var ' + chunks[i].map(function(v) { - var type = getImplementationType(v); - if (!isIllegalType(type) || v.ident.indexOf('$', 1) > 0) { // not illegal, or a broken up illegal - return v.ident + ' = ' + asmInitializer(type); //, func.variables[v.ident].impl); - } else { - return range(Math.ceil(getBits(type)/32)).map(function(i) { - return v.ident + '$' + i + '= 0'; - }).join(','); - } - }).join(', ') + ';\n'; + return v.ident + '=' + asmInitializer(getImplementationType(v)); //, func.variables[v.ident].impl); + }).join(',') + ';\n'; } } } - if (true) { // TODO: optimize away when not needed - if (CLOSURE_ANNOTATIONS) func.JS += '/** @type {number} */'; - func.JS += INDENTATION + 'var label = 0;\n'; + if (CLOSURE_ANNOTATIONS) func.JS += '/** @type {number} */'; + if (!ASM_JS) { + func.JS += INDENTATION + 'var label=0;\n'; } if (ASM_JS) { @@ -632,12 +636,12 @@ function JSify(data, functionsOnly, givenFunctions) { hasByVal = hasByVal || param.byVal; }); if (hasByVal) { - func.JS += INDENTATION + 'var tempParam = 0;\n'; + func.JS += INDENTATION + 'var tempParam=0;\n'; } } if (func.hasVarArgsCall) { - func.JS += INDENTATION + 'var tempVarArgs = 0;\n'; + func.JS += INDENTATION + 'var tempVarArgs=0;\n'; } // Prepare the stack, if we need one. If we have other stack allocations, force the stack to be set up. @@ -654,7 +658,7 @@ function JSify(data, functionsOnly, givenFunctions) { if (param.byVal) { var type = removePointing(param.type); var typeInfo = Types.types[type]; - func.JS += INDENTATION + (ASM_JS ? '' : 'var ') + 'tempParam = ' + param.ident + '; ' + param.ident + ' = ' + RuntimeGenerator.stackAlloc(typeInfo.flatSize) + ';' + + func.JS += INDENTATION + (ASM_JS ? '' : 'var ') + 'tempParam = ' + param.ident + '; ' + param.ident + '=' + RuntimeGenerator.stackAlloc(typeInfo.flatSize) + ';' + makeCopyValues(param.ident, 'tempParam', typeInfo.flatSize, 'null', null, param.byVal) + ';\n'; } }); @@ -665,14 +669,14 @@ function JSify(data, functionsOnly, givenFunctions) { function walkBlock(block, indent) { if (!block) return ''; dprint('relooping', 'walking block: ' + block.type + ',' + block.entries + ' : ' + block.labels.length); - function getLabelLines(label, indent, relooping) { + function getLabelLines(label, relooping) { if (!label) return ''; var ret = ''; if ((LABEL_DEBUG >= 2) && functionNameFilterTest(func.ident)) { - ret += indent + "Module.print(INDENT + '" + func.ident + ":" + label.ident + "');\n"; + ret += INDENTATION + "Module.print(INDENT + '" + func.ident + ":" + label.ident + "');\n"; } if (EXECUTION_TIMEOUT > 0) { - ret += indent + 'if (Date.now() - START_TIME >= ' + (EXECUTION_TIMEOUT*1000) + ') throw "Timed out!" + (new Error().stack);\n'; + ret += INDENTATION + 'if (Date.now() - START_TIME >= ' + (EXECUTION_TIMEOUT*1000) + ') throw "Timed out!" + (new Error().stack);\n'; } if (PRINT_SPLIT_FILE_MARKER && Debugging.on && Debugging.getAssociatedSourceFile(label.lines[label.lines.length-1].lineNum)) { @@ -685,6 +689,7 @@ function JSify(data, functionsOnly, givenFunctions) { var i = 0; return ret + label.lines.map(function(line) { var JS = line.JS; + if (!relooping) JS = INDENTATION + JS; if (relooping && i == label.lines.length-1) { if (line.intertype == 'branch' || line.intertype == 'switch') { JS = ''; // just branching operations - done in the relooper, so nothing need be done here @@ -695,12 +700,10 @@ function JSify(data, functionsOnly, givenFunctions) { i++; // invoke instructions span two lines, and the debug info is located // on the second line, hence the +1 - return JS + (Debugging.on ? Debugging.getComment(line.lineNum + (line.intertype === 'invoke' ? 1 : 0)) : ''); - }) - .join('\n') - .split('\n') // some lines include line breaks - .map(function(line) { return indent + line }) - .join('\n'); + if (Debugging.on) JS += Debugging.getComment(line.lineNum + (line.intertype === 'invoke' ? 1 : 0)); + //assert(JS.indexOf('\n') < 0, JS); + return JS; + }).join('\n'); } var ret = ''; if (!RELOOP || func.forceEmulated) { // TODO: also if just 1 label? @@ -719,19 +722,19 @@ function JSify(data, functionsOnly, givenFunctions) { ret += 'dummy: 0'; ret += '};\n'; } else { - ret += 'var setjmpLabel = 0;\n'; - ret += 'var setjmpTable = ' + RuntimeGenerator.stackAlloc(4 * (MAX_SETJMPS + 1) * 2) + ';\n'; + ret += makeVarDef('setjmpLabel') + '=0;\n'; + ret += makeVarDef('setjmpTable') + '=' + RuntimeGenerator.stackAlloc(4 * (MAX_SETJMPS + 1) * 2) + ';\n'; ret += makeSetValue('setjmpTable', '0', '0', 'i32') + ';'; // initialize first entry to 0 } } - ret += indent + 'while(1) '; + ret += indent + 'while(1)'; if (func.setjmpTable && !ASM_JS) { ret += 'try { '; } - ret += 'switch(' + asmCoercion('label', 'i32') + ') {\n'; + ret += 'switch(' + asmCoercion('label', 'i32') + '){\n'; ret += block.labels.map(function(label) { - return indent + INDENTATION + 'case ' + getLabelId(label.ident) + ': ' + (SHOW_LABELS ? '// ' + getOriginalLabelId(label.ident) : '') + '\n' - + getLabelLines(label, indent + INDENTATION + INDENTATION); + return INDENTATION + 'case ' + getLabelId(label.ident) + ': ' + (SHOW_LABELS ? '// ' + getOriginalLabelId(label.ident) : '') + '\n' + + getLabelLines(label); }).join('\n') + '\n'; if (func.setjmpTable && ASM_JS) { // emit a label in which we write to the proper local variable, before jumping to the actual label @@ -748,8 +751,16 @@ function JSify(data, functionsOnly, givenFunctions) { if (func.setjmpTable && !ASM_JS) { ret += ' } catch(e) { if (!e.longjmp || !(e.id in mySetjmpIds)) throw(e); setjmpTable[setjmpLabels[e.id]](e.value) }'; } + if (ASM_JS && func.returnType !== 'void') { + // Add a return + if (func.returnType in Runtime.FLOAT_TYPES) { + ret += ' return +0;\n'; + } else { + ret += ' return 0;\n'; + } + } } else { - ret += (SHOW_LABELS ? indent + '/* ' + block.entries[0] + ' */' : '') + '\n' + getLabelLines(block.labels[0], indent); + ret += (SHOW_LABELS ? indent + '/* ' + block.entries[0] + ' */' : '') + '\n' + getLabelLines(block.labels[0]); } ret += '\n'; } else { @@ -764,7 +775,7 @@ function JSify(data, functionsOnly, givenFunctions) { // add blocks for (var i = 0; i < block.labels.length; i++) { var label = block.labels[i]; - var content = getLabelLines(label, '', true); + var content = getLabelLines(label, true); //printErr(func.ident + ' : ' + label.ident + ' : ' + content + '\n'); var last = label.lines[label.lines.length-1]; if (!last.signedIdent) { @@ -810,9 +821,17 @@ function JSify(data, functionsOnly, givenFunctions) { // Finalize function if (LABEL_DEBUG && functionNameFilterTest(func.ident)) func.JS += " INDENT = INDENT.substr(0, INDENT.length-2);\n"; // Ensure a return in a function with a type that returns, even if it lacks a return (e.g., if it aborts()) - if (RELOOP && func.lines.length > 0 && func.returnType != 'void') { - var returns = func.labels.filter(function(label) { return label.lines[label.lines.length-1].intertype == 'return' }).length; - if (returns == 0) func.JS += INDENTATION + 'return ' + asmCoercion('0', func.returnType); + if (RELOOP && ASM_JS && func.lines.length > 0 && func.returnType != 'void') { + var lastCurly = func.JS.lastIndexOf('}'); + var lastReturn = func.JS.lastIndexOf('return '); + if ((lastCurly < 0 && lastReturn < 0) || // no control flow, no return + (lastCurly >= 0 && lastReturn < lastCurly)) { // control flow, no return past last join + if (func.returnType in Runtime.FLOAT_TYPES) { + func.JS += ' return +0;\n'; + } else { + func.JS += ' return 0;\n'; + } + } } func.JS += '}\n'; @@ -898,6 +917,11 @@ function JSify(data, functionsOnly, givenFunctions) { // Function lines function valueHandler(item) { + if (item.vars) { + item.vars.forEach(function(v) { + addVariable(v[0], v[1]); + }); + } return item.ident; } function noopHandler(item) { @@ -959,7 +983,7 @@ function JSify(data, functionsOnly, givenFunctions) { var parts = label.split('|'); var trueLabel = parts[1] || ''; var oldLabel = parts[2] || ''; - var labelSetting = oldLabel ? 'label = ' + getLabelId(oldLabel) + ';' + + var labelSetting = oldLabel ? 'label=' + getLabelId(oldLabel) + ';' + (SHOW_LABELS ? ' /* to: ' + getOriginalLabelId(cleanLabel(oldLabel)) + ' */' : '') : ''; // TODO: optimize away the setting if (label[1] == 'R') { if (label[2] == 'N') { // BRNOL: break, no label setting @@ -980,7 +1004,7 @@ function JSify(data, functionsOnly, givenFunctions) { } } else { if (!labelIsVariable) label = getLabelId(label); - return pre + 'label = ' + label + ';' + (SHOW_LABELS ? ' /* to: ' + getOriginalLabelId(cleanLabel(label)) + ' */' : '') + ' break;'; + return pre + 'label=' + label + ';' + (SHOW_LABELS ? ' /* to: ' + getOriginalLabelId(cleanLabel(label)) + ' */' : '') + 'break;'; } } @@ -1002,14 +1026,14 @@ function JSify(data, functionsOnly, givenFunctions) { var labelSets = phiSets[label]; // FIXME: Many of the |var |s here are not needed, but without them we get slowdowns with closure compiler. TODO: remove this workaround. if (labelSets.length == 1) { - return (ASM_JS ? '' : 'var ') + labelSets[0].ident + ' = ' + labelSets[0].valueJS + ';'; + return (ASM_JS ? '' : 'var ') + labelSets[0].ident + '=' + labelSets[0].valueJS + ';'; } // TODO: eliminate unneeded sets (to undefined etc.) var deps = {}; // for each ident we will set, which others it depends on - var valueJSes = {}; + var map = {}; labelSets.forEach(function(labelSet) { deps[labelSet.ident] = {}; - valueJSes[labelSet.ident] = labelSet.valueJS; + map[labelSet.ident] = labelSet; }); labelSets.forEach(function(labelSet) { walkInterdata(labelSet.value, function mark(item) { @@ -1028,14 +1052,18 @@ function JSify(data, functionsOnly, givenFunctions) { } for (var i = 0; i < idents.length; i++) { if (keys(deps[idents[i]]).length == 0) { - post = 'var ' + idents[i] + ' = ' + valueJSes[idents[i]] + ';' + post; + post = idents[i] + '=' + map[idents[i]].valueJS + ';' + post; + if (!ASM_JS) post = 'var ' + post; + else addVariable(idents[i], map[idents[i]].value.type); remove(idents[i]); continue mainLoop; } } // If we got here, we have circular dependencies, and must break at least one. - pre += 'var ' + idents[0] + '$phi = ' + valueJSes[idents[0]] + ';'; - post += 'var ' + idents[0] + ' = ' + idents[0] + '$phi;'; + pre += makeVarDef(idents[0]) + '$phi=' + map[idents[0]].valueJS + ';'; + post += makeVarDef(idents[0]) + '=' + idents[0] + '$phi;'; + addVariable(idents[0] + '$phi', map[idents[0]].value.type); + addVariable(idents[0], map[idents[0]].value.type); remove(idents[0]); } return pre + post; @@ -1062,10 +1090,10 @@ function JSify(data, functionsOnly, givenFunctions) { var labelTrue = (item.labelTrueJS = getPhiSetsForLabel(phiSets, item.labelTrue)) + makeBranch(item.labelTrue, item.currLabelId); var labelFalse = (item.labelFalseJS = getPhiSetsForLabel(phiSets, item.labelFalse)) + makeBranch(item.labelFalse, item.currLabelId); if (labelTrue == ';' && labelFalse == ';') return ';'; - var head = 'if (' + condition + ') { '; - var head2 = 'if (!(' + condition + ')) { '; - var else_ = ' } else { '; - var tail = ' }'; + var head = 'if(' + condition + '){'; + var head2 = 'if(!(' + condition + ')){'; + var else_ = '}else{'; + var tail = '}'; if (labelTrue == ';') { return head2 + labelFalse + tail; } else if (labelFalse == ';') { @@ -1112,7 +1140,7 @@ function JSify(data, functionsOnly, givenFunctions) { item.groupedLabels = []; } if (!useIfs) { - ret += 'switch(' + signedIdent + ') {\n'; + ret += 'switch(' + signedIdent + '){'; } // process target labels, sorting them so output is consistently ordered keys(targetLabels).sort().forEach(function(targetLabel) { @@ -1125,17 +1153,17 @@ function JSify(data, functionsOnly, givenFunctions) { if (useIfs) { value = targetLabels[targetLabel].map(function(value) { return makeComparison(signedIdent, '==', makeSignOp(value, item.type, 're'), item.type) - }).join(' | '); - ret += 'if (' + value + ') {\n'; + }).join('|'); + ret += 'if(' + value + '){'; } else { value = targetLabels[targetLabel].map(function(value) { return 'case ' + makeSignOp(value, item.type, 're') + ':'; - }).join(' '); - ret += value + '{\n'; + }).join(''); + ret += value + '{'; } var phiSet = getPhiSetsForLabel(phiSets, targetLabel); - ret += INDENTATION + '' + phiSet + makeBranch(targetLabel, item.currLabelId || null) + '\n'; - ret += '}\n'; + ret += INDENTATION + '' + phiSet + makeBranch(targetLabel, item.currLabelId || null); + ret += '}'; if (RELOOP) { item.groupedLabels.push({ label: targetLabel, @@ -1146,15 +1174,15 @@ function JSify(data, functionsOnly, givenFunctions) { }); var phiSet = item.defaultLabelJS = getPhiSetsForLabel(phiSets, item.defaultLabel); if (useIfs) { - if (item.switchLabels.length > 0) ret += 'else {\n'; - ret += phiSet + makeBranch(item.defaultLabel, item.currLabelId) + '\n'; - if (item.switchLabels.length > 0) ret += '}\n'; + if (item.switchLabels.length > 0) ret += 'else{'; + ret += phiSet + makeBranch(item.defaultLabel, item.currLabelId) + ''; + if (item.switchLabels.length > 0) ret += '}'; } else { - ret += 'default: {\n'; - ret += phiSet + makeBranch(item.defaultLabel, item.currLabelId) + '\n'; - ret += '}\n'; + ret += 'default:{'; + ret += phiSet + makeBranch(item.defaultLabel, item.currLabelId) + ''; + ret += '}'; - ret += '} break; \n'; // finish switch and break, to move control flow properly (breaks from makeBranch just broke out of the switch) + ret += '}break;'; // finish switch and break, to move control flow properly (breaks from makeBranch just broke out of the switch) } if (item.value) { ret += ' ' + toNiceIdent(item.value); @@ -1162,10 +1190,11 @@ function JSify(data, functionsOnly, givenFunctions) { return ret; } function returnHandler(item) { - var ret = RuntimeGenerator.stackExit(item.funcData.initialStack, item.funcData.otherStackAllocations) + ';\n'; + var ret = RuntimeGenerator.stackExit(item.funcData.initialStack, item.funcData.otherStackAllocations); + if (ret.length > 0) ret += ';'; if (LABEL_DEBUG && functionNameFilterTest(item.funcData.ident)) { - ret += "Module.print(INDENT + 'Exiting: " + item.funcData.ident + "');\n" - + "INDENT = INDENT.substr(0, INDENT.length-2);\n"; + ret += "Module.print(INDENT + 'Exiting: " + item.funcData.ident + "');" + + "INDENT = INDENT.substr(0, INDENT.length-2);"; } ret += 'return'; var value = item.value ? finalizeLLVMParameter(item.value) : null; @@ -1191,7 +1220,7 @@ function JSify(data, functionsOnly, givenFunctions) { // in an assignment var disabled = DISABLE_EXCEPTION_CATCHING == 2 && !(item.funcData.ident in EXCEPTION_CATCHING_WHITELIST); var phiSets = calcPhiSets(item); - var call_ = makeFunctionCall(item.ident, item.params, item.funcData, item.type, ASM_JS && !disabled, !!item.assignTo || !item.standalone, true); + var call_ = makeFunctionCall(item, item.params, item.funcData, item.type, ASM_JS && !disabled, !!item.assignTo || !item.standalone, true); var ret; @@ -1217,11 +1246,16 @@ function JSify(data, functionsOnly, givenFunctions) { ret = makeVarArgsCleanup(ret); if (item.assignTo) { - ret = 'var ' + item.assignTo + ' = ' + ret; - if (USE_TYPED_ARRAYS == 2 && isIllegalType(item.type)) { + var illegal = USE_TYPED_ARRAYS == 2 && isIllegalType(item.type); + var assignTo = illegal ? item.assignTo + '$r' : item.assignTo; + ret = makeVarDef(assignTo) + '=' + ret; + if (ASM_JS) addVariable(assignTo, item.type); + if (illegal) { var bits = getBits(item.type); for (var i = 0; i < bits/32; i++) { - ret += 'var ' + item.assignTo + '$' + i + ' = ' + (i == 0 ? item.assignTo : 'tempRet' + (i-1)) + ';' + var v = item.assignTo + '$' + i; + ret += makeVarDef(v) + '=' + (i == 0 ? assignTo : 'tempRet' + (i-1)) + ';' + if (ASM_JS) addVariable(v, 'i32'); } } item.assignTo = null; @@ -1252,8 +1286,12 @@ function JSify(data, functionsOnly, givenFunctions) { } } function landingpadHandler(item) { + if (ASM_JS) { + addVariable(item.assignTo + '$0', 'i32'); + addVariable(item.assignTo + '$1', 'i32'); + } if (DISABLE_EXCEPTION_CATCHING && !(item.funcData.ident in EXCEPTION_CATCHING_WHITELIST) && USE_TYPED_ARRAYS == 2) { - ret = makeVarDef(item.assignTo) + '$0 = 0; ' + item.assignTo + '$1 = 0;'; + ret = makeVarDef(item.assignTo) + '$0 = 0; ' + makeVarDef(item.assignTo) + '$1 = 0;'; item.assignTo = null; if (VERBOSE) warnOnce('landingpad, but exceptions are disabled!'); return ret; @@ -1261,7 +1299,7 @@ function JSify(data, functionsOnly, givenFunctions) { var catchTypeArray = item.catchables.map(finalizeLLVMParameter).map(function(element) { return asmCoercion(element, 'i32') }).join(','); var ret = asmCoercion('___cxa_find_matching_catch(-1, -1' + (catchTypeArray.length > 0 ? ',' + catchTypeArray : '') +')', 'i32'); if (USE_TYPED_ARRAYS == 2) { - ret = makeVarDef(item.assignTo) + '$0 = ' + ret + '; ' + item.assignTo + '$1 = tempRet0;'; + ret = makeVarDef(item.assignTo) + '$0 = ' + ret + '; ' + makeVarDef(item.assignTo) + '$1 = tempRet0;'; item.assignTo = null; } return ret; @@ -1307,13 +1345,14 @@ function JSify(data, functionsOnly, givenFunctions) { item.ident = 'tempValue'; ret += item.ident + ' = [' + makeEmptyStruct(item.type) + '], '; } - return ret + item.ident + '.f' + item.indexes[0][0].text + ' = ' + finalizeLLVMParameter(item.value) + ', ' + item.ident + ')'; + return ret + item.ident + '.f' + item.indexes[0][0].text + '=' + finalizeLLVMParameter(item.value) + ', ' + item.ident + ')'; } function indirectbrHandler(item) { var phiSets = calcPhiSets(item); - var js = 'var ibr = ' + finalizeLLVMParameter(item.value) + ';\n'; + var js = makeVarDef('ibr') + '=' + finalizeLLVMParameter(item.value) + ';'; + if (ASM_JS) addVariable('ibr', 'i32'); for (var targetLabel in phiSets) { - js += 'if (' + makeComparison('ibr', '==', targetLabel, 'i32') + ') { ' + getPhiSetsForLabel(phiSets, targetLabel) + ' }\n'; + js += 'if(' + makeComparison('ibr', '==', targetLabel, 'i32') + '){' + getPhiSetsForLabel(phiSets, targetLabel) + '}'; } return js + makeBranch('ibr', item.currLabelId, true); } @@ -1349,7 +1388,9 @@ function JSify(data, functionsOnly, givenFunctions) { return ret; } - function makeFunctionCall(ident, params, funcData, type, forceByPointer, hasReturn, invoke) { + function makeFunctionCall(item, params, funcData, type, forceByPointer, hasReturn, invoke) { + var ident = item.ident; + // We cannot compile assembly. See comment in intertyper.js:'Call' assert(ident != 'asm', 'Inline assembly cannot be compiled to JavaScript!'); @@ -1368,7 +1409,7 @@ function JSify(data, functionsOnly, givenFunctions) { var callIdent = LibraryManager.getRootIdent(simpleIdent); if (callIdent) { simpleIdent = callIdent; // ident may not be in library, if all there is is ident__inline, but in this case it is - if (callIdent.indexOf('.') < 0) { + if (callIdent.indexOf('Math_') !== 0) { callIdent = '_' + callIdent; // Not Math.*, so add the normal prefix } } else { @@ -1499,6 +1540,9 @@ function JSify(data, functionsOnly, givenFunctions) { if (trueType !== returnType && !isIdenticallyImplemented(trueType, returnType)) { if (VERBOSE) warnOnce('Fixing function call based on return type from signature, on ' + [callIdent, returnType, trueType]); returnType = trueType; + if (trueType === 'void') { + item.assignTo = null; + } } } } @@ -1526,7 +1570,7 @@ function JSify(data, functionsOnly, givenFunctions) { if (!ASM_JS || functionTableCall) callIdent = Functions.getTable(sig) + '[' + callIdent + ']'; } - var ret = callIdent + '(' + args.join(', ') + ')'; + var ret = callIdent + '(' + args.join(',') + ')'; if (ASM_JS) { // TODO: do only when needed (library functions and Math.*?) XXX && simpleIdent in Functions.libraryFunctions) { ret = asmCoercion(ret, returnType); if (simpleIdent == 'abort' && funcData.returnType != 'void') { @@ -1537,7 +1581,7 @@ function JSify(data, functionsOnly, givenFunctions) { if (ASM_JS && funcData.setjmpTable) { // check if a longjmp was done. If a setjmp happened, check if ours. If ours, go to a special label to handle it. // otherwise, just return - the call to us must also have been an invoke, so the setjmp propagates that way - ret += '; if (((__THREW__|0) != 0) & ((threwValue|0) != 0)) { setjmpLabel = ' + asmCoercion('_testSetjmp(' + makeGetValue('__THREW__', 0, 'i32') + ', setjmpTable)', 'i32') + '; if ((setjmpLabel|0) > 0) { label = ' + SETJMP_LABEL + '; break } else return ' + (funcData.returnType != 'void' ? asmCoercion('0', funcData.returnType) : '') + ' } __THREW__ = threwValue = 0;\n'; + ret += '; if (((__THREW__|0) != 0) & ((threwValue|0) != 0)) { setjmpLabel = ' + asmCoercion('_testSetjmp(' + makeGetValue('__THREW__', 0, 'i32') + ', setjmpTable)', 'i32') + '; if ((setjmpLabel|0) > 0) { label = ' + SETJMP_LABEL + '; break } else return ' + (funcData.returnType != 'void' ? asmCoercion('0', funcData.returnType) : '') + ' } __THREW__ = threwValue = 0;'; } return ret; @@ -1558,7 +1602,7 @@ function JSify(data, functionsOnly, givenFunctions) { function getelementptrHandler(item) { return finalizeLLVMFunctionCall(item) } function callHandler(item) { if (item.standalone && LibraryManager.isStubFunction(item.ident)) return ';'; - var ret = makeFunctionCall(item.ident, item.params, item.funcData, item.type, false, !!item.assignTo || !item.standalone) + (item.standalone ? ';' : ''); + var ret = makeFunctionCall(item, item.params, item.funcData, item.type, false, !!item.assignTo || !item.standalone) + (item.standalone ? ';' : ''); return makeVarArgsCleanup(ret); } @@ -1805,14 +1849,14 @@ function JSify(data, functionsOnly, givenFunctions) { var shellParts = read(shellFile).split('{{BODY}}'); print(processMacros(preprocess(shellParts[1]))); // Print out some useful metadata - if (EMIT_GENERATED_FUNCTIONS || PGO) { + if (RUNNING_JS_OPTS || PGO) { var generatedFunctions = JSON.stringify(keys(Functions.implementedFunctions).filter(function(func) { return IGNORED_FUNCTIONS.indexOf(func.ident) < 0; })); if (PGO) { print('PGOMonitor.allGenerated = ' + generatedFunctions + ';\nremoveRunDependency("pgo");\n'); } - if (EMIT_GENERATED_FUNCTIONS) { + if (RUNNING_JS_OPTS) { print('// EMSCRIPTEN_GENERATED_FUNCTIONS: ' + generatedFunctions + '\n'); } } diff --git a/src/library.js b/src/library.js index 5c2c858d..5e71b087 100644 --- a/src/library.js +++ b/src/library.js @@ -28,13 +28,7 @@ LibraryManager.library = { // dirent.h // ========================================================================== - __dirent_struct_layout: Runtime.generateStructInfo([ - ['i32', 'd_ino'], - ['i32', 'd_off'], - ['i16', 'd_reclen'], - ['i8', 'd_type'], - ['b256', 'd_name']]), - opendir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', '__dirent_struct_layout', 'open'], + opendir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'open'], opendir: function(dirname) { // DIR *opendir(const char *dirname); // http://pubs.opengroup.org/onlinepubs/007908799/xsh/opendir.html @@ -90,7 +84,7 @@ LibraryManager.library = { // http://pubs.opengroup.org/onlinepubs/007908799/xsh/rewinddir.html _seekdir(dirp, 0); }, - readdir_r__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', '__dirent_struct_layout'], + readdir_r__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], readdir_r: function(dirp, entry, result) { // int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result); // http://pubs.opengroup.org/onlinepubs/007908799/xsh/readdir_r.html @@ -123,14 +117,14 @@ LibraryManager.library = { FS.isLink(child.mode) ? 10 : // DT_LNK, symbolic link. 8; // DT_REG, regular file. } - {{{ makeSetValue('entry', '___dirent_struct_layout.d_ino', 'id', 'i32') }}} - {{{ makeSetValue('entry', '___dirent_struct_layout.d_off', 'offset', 'i32') }}} - {{{ makeSetValue('entry', '___dirent_struct_layout.d_reclen', 'name.length + 1', 'i32') }}} + {{{ makeSetValue('entry', C_STRUCTS.dirent.d_ino, 'id', 'i32') }}} + {{{ makeSetValue('entry', C_STRUCTS.dirent.d_off, 'offset', 'i32') }}} + {{{ makeSetValue('entry', C_STRUCTS.dirent.d_reclen, 'name.length + 1', 'i32') }}} for (var i = 0; i < name.length; i++) { - {{{ makeSetValue('entry + ___dirent_struct_layout.d_name', 'i', 'name.charCodeAt(i)', 'i8') }}} + {{{ makeSetValue('entry + ' + C_STRUCTS.dirent.d_name, 'i', 'name.charCodeAt(i)', 'i8') }}} } - {{{ makeSetValue('entry + ___dirent_struct_layout.d_name', 'i', '0', 'i8') }}} - {{{ makeSetValue('entry', '___dirent_struct_layout.d_type', 'type', 'i8') }}} + {{{ makeSetValue('entry + ' + C_STRUCTS.dirent.d_name, 'i', '0', 'i8') }}} + {{{ makeSetValue('entry', C_STRUCTS.dirent.d_type, 'type', 'i8') }}} {{{ makeSetValue('result', '0', 'entry', 'i8*') }}} stream.position++; return 0; @@ -145,7 +139,7 @@ LibraryManager.library = { return 0; } // TODO Is it supposed to be safe to execute multiple readdirs? - if (!_readdir.entry) _readdir.entry = _malloc(___dirent_struct_layout.__size__); + if (!_readdir.entry) _readdir.entry = _malloc({{{ C_STRUCTS.dirent.__size__ }}}); if (!_readdir.result) _readdir.result = _malloc(4); var err = _readdir_r(dirp, _readdir.entry, _readdir.result); if (err) { @@ -161,17 +155,14 @@ LibraryManager.library = { // utime.h // ========================================================================== - __utimbuf_struct_layout: Runtime.generateStructInfo([ - ['i32', 'actime'], - ['i32', 'modtime']]), - utime__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', '__utimbuf_struct_layout'], + utime__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], utime: function(path, times) { // int utime(const char *path, const struct utimbuf *times); // http://pubs.opengroup.org/onlinepubs/009695399/basedefs/utime.h.html var time; if (times) { // NOTE: We don't keep track of access timestamps. - var offset = ___utimbuf_struct_layout.modtime; + var offset = {{{ C_STRUCTS.utimbuf.modtime }}}; time = {{{ makeGetValue('times', 'offset', 'i32') }}} time *= 1000; } else { @@ -253,27 +244,7 @@ LibraryManager.library = { // sys/stat.h // ========================================================================== - __stat_struct_layout: Runtime.generateStructInfo([ - ['i32', 'st_dev'], - ['i32', '__st_dev_padding'], - ['i32', '__st_ino_truncated'], - ['i32', 'st_mode'], - ['i32', 'st_nlink'], - ['i32', 'st_uid'], - ['i32', 'st_gid'], - ['i32', 'st_rdev'], - ['i32', '__st_rdev_padding'], - ['i32', 'st_size'], - ['i32', 'st_blksize'], - ['i32', 'st_blocks'], - ['i32', 'st_atim_secs'], - ['i32', 'st_atim_nsecs'], - ['i32', 'st_mtim_secs'], - ['i32', 'st_mtim_nsecs'], - ['i32', 'st_ctim_secs'], - ['i32', 'st_ctim_nsecs'], - ['i32', 'st_ino']]), - stat__deps: ['$FS', '__stat_struct_layout'], + stat__deps: ['$FS'], stat: function(path, buf, dontResolveLastLink) { // http://pubs.opengroup.org/onlinepubs/7908799/xsh/stat.html // int stat(const char *path, struct stat *buf); @@ -282,25 +253,25 @@ LibraryManager.library = { path = typeof path !== 'string' ? Pointer_stringify(path) : path; try { var stat = dontResolveLastLink ? FS.lstat(path) : FS.stat(path); - {{{ makeSetValue('buf', '___stat_struct_layout.st_dev', 'stat.dev', 'i32') }}}; - {{{ makeSetValue('buf', '___stat_struct_layout.__st_dev_padding', '0', 'i32') }}}; - {{{ makeSetValue('buf', '___stat_struct_layout.__st_ino_truncated', 'stat.ino', 'i32') }}}; - {{{ makeSetValue('buf', '___stat_struct_layout.st_mode', 'stat.mode', 'i32') }}} - {{{ makeSetValue('buf', '___stat_struct_layout.st_nlink', 'stat.nlink', 'i32') }}} - {{{ makeSetValue('buf', '___stat_struct_layout.st_uid', 'stat.uid', 'i32') }}} - {{{ makeSetValue('buf', '___stat_struct_layout.st_gid', 'stat.gid', 'i32') }}} - {{{ makeSetValue('buf', '___stat_struct_layout.st_rdev', 'stat.rdev', 'i32') }}} - {{{ makeSetValue('buf', '___stat_struct_layout.__st_rdev_padding', '0', 'i32') }}}; - {{{ makeSetValue('buf', '___stat_struct_layout.st_size', 'stat.size', 'i32') }}} - {{{ makeSetValue('buf', '___stat_struct_layout.st_blksize', '4096', 'i32') }}} - {{{ makeSetValue('buf', '___stat_struct_layout.st_blocks', 'stat.blocks', 'i32') }}} - {{{ makeSetValue('buf', '___stat_struct_layout.st_atim_secs', 'Math.floor(stat.atime.getTime() / 1000)', 'i32') }}} - {{{ makeSetValue('buf', '___stat_struct_layout.st_atim_nsecs', '0', 'i32') }}} - {{{ makeSetValue('buf', '___stat_struct_layout.st_mtim_secs', 'Math.floor(stat.mtime.getTime() / 1000)', 'i32') }}} - {{{ makeSetValue('buf', '___stat_struct_layout.st_mtim_nsecs', '0', 'i32') }}} - {{{ makeSetValue('buf', '___stat_struct_layout.st_ctim_secs', 'Math.floor(stat.ctime.getTime() / 1000)', 'i32') }}} - {{{ makeSetValue('buf', '___stat_struct_layout.st_ctim_nsecs', '0', 'i32') }}} - {{{ makeSetValue('buf', '___stat_struct_layout.st_ino', 'stat.ino', 'i32') }}} + {{{ makeSetValue('buf', C_STRUCTS.stat.st_dev, 'stat.dev', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.stat.__st_dev_padding, '0', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.stat.__st_ino_truncated, 'stat.ino', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.stat.st_mode, 'stat.mode', 'i32') }}} + {{{ makeSetValue('buf', C_STRUCTS.stat.st_nlink, 'stat.nlink', 'i32') }}} + {{{ makeSetValue('buf', C_STRUCTS.stat.st_uid, 'stat.uid', 'i32') }}} + {{{ makeSetValue('buf', C_STRUCTS.stat.st_gid, 'stat.gid', 'i32') }}} + {{{ makeSetValue('buf', C_STRUCTS.stat.st_rdev, 'stat.rdev', 'i32') }}} + {{{ makeSetValue('buf', C_STRUCTS.stat.__st_rdev_padding, '0', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.stat.st_size, 'stat.size', 'i32') }}} + {{{ makeSetValue('buf', C_STRUCTS.stat.st_blksize, '4096', 'i32') }}} + {{{ makeSetValue('buf', C_STRUCTS.stat.st_blocks, 'stat.blocks', 'i32') }}} + {{{ makeSetValue('buf', C_STRUCTS.stat.st_atim.tv_sec, 'Math.floor(stat.atime.getTime() / 1000)', 'i32') }}} + {{{ makeSetValue('buf', C_STRUCTS.stat.st_atim.tv_nsec, '0', 'i32') }}} + {{{ makeSetValue('buf', C_STRUCTS.stat.st_mtim.tv_sec, 'Math.floor(stat.mtime.getTime() / 1000)', 'i32') }}} + {{{ makeSetValue('buf', C_STRUCTS.stat.st_mtim.tv_nsec, '0', 'i32') }}} + {{{ makeSetValue('buf', C_STRUCTS.stat.st_ctim.tv_sec, 'Math.floor(stat.ctime.getTime() / 1000)', 'i32') }}} + {{{ makeSetValue('buf', C_STRUCTS.stat.st_ctim.tv_nsec, '0', 'i32') }}} + {{{ makeSetValue('buf', C_STRUCTS.stat.st_ino, 'stat.ino', 'i32') }}} return 0; } catch (e) { FS.handleFSError(e); @@ -436,43 +407,23 @@ LibraryManager.library = { // sys/statvfs.h // ========================================================================== - __statvfs_struct_layout: Runtime.generateStructInfo([ - ['i32', 'f_bsize'], - ['i32', 'f_frsize'], - ['i32', 'f_blocks'], - ['i32', 'f_bfree'], - ['i32', 'f_bavail'], - ['i32', 'f_files'], - ['i32', 'f_ffree'], - ['i32', 'f_favail'], - ['i32', 'f_fsid'], - ['i32', '__padding'], - ['i32', 'f_flag'], - ['i32', 'f_namemax'], - ['i32', '__reserved_1'], - ['i32', '__reserved_2'], - ['i32', '__reserved_3'], - ['i32', '__reserved_4'], - ['i32', '__reserved_5'], - ['i32', '__reserved_6']]), - statvfs__deps: ['$FS', '__statvfs_struct_layout'], + statvfs__deps: ['$FS'], statvfs: function(path, buf) { // http://pubs.opengroup.org/onlinepubs/009695399/functions/statvfs.html // int statvfs(const char *restrict path, struct statvfs *restrict buf); - var offsets = ___statvfs_struct_layout; // NOTE: None of the constants here are true. We're just returning safe and // sane values. - {{{ makeSetValue('buf', 'offsets.f_bsize', '4096', 'i32') }}} - {{{ makeSetValue('buf', 'offsets.f_frsize', '4096', 'i32') }}} - {{{ makeSetValue('buf', 'offsets.f_blocks', '1000000', 'i32') }}} - {{{ makeSetValue('buf', 'offsets.f_bfree', '500000', 'i32') }}} - {{{ makeSetValue('buf', 'offsets.f_bavail', '500000', 'i32') }}} - {{{ makeSetValue('buf', 'offsets.f_files', 'FS.nextInode', 'i32') }}} - {{{ makeSetValue('buf', 'offsets.f_ffree', '1000000', 'i32') }}} - {{{ makeSetValue('buf', 'offsets.f_favail', '1000000', 'i32') }}} - {{{ makeSetValue('buf', 'offsets.f_fsid', '42', 'i32') }}} - {{{ makeSetValue('buf', 'offsets.f_flag', '2', 'i32') }}} // ST_NOSUID - {{{ makeSetValue('buf', 'offsets.f_namemax', '255', 'i32') }}} + {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_bsize, '4096', 'i32') }}} + {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_frsize, '4096', 'i32') }}} + {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_blocks, '1000000', 'i32') }}} + {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_bfree, '500000', 'i32') }}} + {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_bavail, '500000', 'i32') }}} + {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_files, 'FS.nextInode', 'i32') }}} + {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_ffree, '1000000', 'i32') }}} + {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_favail, '1000000', 'i32') }}} + {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_fsid, '42', 'i32') }}} + {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_flag, '2', 'i32') }}} // ST_NOSUID + {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_namemax, '255', 'i32') }}} return 0; }, fstatvfs__deps: ['statvfs'], @@ -488,13 +439,7 @@ LibraryManager.library = { // fcntl.h // ========================================================================== - __flock_struct_layout: Runtime.generateStructInfo([ - ['i16', 'l_type'], - ['i16', 'l_whence'], - ['i32', 'l_start'], - ['i32', 'l_len'], - ['i16', 'l_pid']]), - open__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', '__dirent_struct_layout'], + open__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], open: function(path, oflag, varargs) { // int open(const char *path, int oflag, ...); // http://pubs.opengroup.org/onlinepubs/009695399/functions/open.html @@ -514,16 +459,19 @@ LibraryManager.library = { // http://pubs.opengroup.org/onlinepubs/009695399/functions/creat.html return _open(path, {{{ cDefine('O_WRONLY') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_TRUNC') }}}, allocate([mode, 0, 0, 0], 'i32', ALLOC_STACK)); }, - mkstemp__deps: ['creat'], - mkstemp: function(template) { - if (!_mkstemp.counter) _mkstemp.counter = 0; - var c = (_mkstemp.counter++).toString(); + mktemp: function(template) { + if (!_mktemp.counter) _mktemp.counter = 0; + var c = (_mktemp.counter++).toString(); var rep = 'XXXXXX'; while (c.length < rep.length) c = '0' + c; writeArrayToMemory(intArrayFromString(c), template + Pointer_stringify(template).indexOf(rep)); - return _creat(template, 0600); + return template; + }, + mkstemp__deps: ['creat', 'mktemp'], + mkstemp: function(template) { + return _creat(_mktemp(template), 0600); }, - fcntl__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', '__flock_struct_layout'], + fcntl__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], fcntl: function(fildes, cmd, varargs, dup2) { // int fcntl(int fildes, int cmd, ...); // http://pubs.opengroup.org/onlinepubs/009695399/functions/fcntl.html @@ -559,7 +507,7 @@ LibraryManager.library = { case {{{ cDefine('F_GETLK') }}}: case {{{ cDefine('F_GETLK64') }}}: var arg = {{{ makeGetValue('varargs', 0, 'i32') }}}; - var offset = ___flock_struct_layout.l_type; + var offset = {{{ C_STRUCTS.flock.l_type }}}; // We're always unlocked. {{{ makeSetValue('arg', 'offset', cDefine('F_UNLCK'), 'i16') }}} return 0; @@ -621,20 +569,15 @@ LibraryManager.library = { // ========================================================================== __DEFAULT_POLLMASK: {{{ cDefine('POLLIN') }}} | {{{ cDefine('POLLOUT') }}}, - __pollfd_struct_layout: Runtime.generateStructInfo([ - ['i32', 'fd'], - ['i16', 'events'], - ['i16', 'revents']]), - poll__deps: ['$FS', '__DEFAULT_POLLMASK', '__pollfd_struct_layout'], + poll__deps: ['$FS', '__DEFAULT_POLLMASK'], poll: function(fds, nfds, timeout) { // int poll(struct pollfd fds[], nfds_t nfds, int timeout); // http://pubs.opengroup.org/onlinepubs/009695399/functions/poll.html - var offsets = ___pollfd_struct_layout; var nonzero = 0; for (var i = 0; i < nfds; i++) { - var pollfd = fds + ___pollfd_struct_layout.__size__ * i; - var fd = {{{ makeGetValue('pollfd', 'offsets.fd', 'i32') }}}; - var events = {{{ makeGetValue('pollfd', 'offsets.events', 'i16') }}}; + var pollfd = fds + {{{ C_STRUCTS.pollfd.__size__ }}} * i; + var fd = {{{ makeGetValue('pollfd', C_STRUCTS.pollfd.fd, 'i32') }}}; + var events = {{{ makeGetValue('pollfd', C_STRUCTS.pollfd.events, 'i16') }}}; var mask = {{{ cDefine('POLLNVAL') }}}; var stream = FS.getStream(fd); if (stream) { @@ -645,7 +588,7 @@ LibraryManager.library = { } mask &= events | {{{ cDefine('POLLERR') }}} | {{{ cDefine('POLLHUP') }}}; if (mask) nonzero++; - {{{ makeSetValue('pollfd', 'offsets.revents', 'mask', 'i16') }}} + {{{ makeSetValue('pollfd', C_STRUCTS.pollfd.revents, 'mask', 'i16') }}} } return nonzero; }, @@ -688,24 +631,13 @@ LibraryManager.library = { // http://pubs.opengroup.org/onlinepubs/000095399/functions/chdir.html // NOTE: The path argument may be a string, to simplify fchdir(). if (typeof path !== 'string') path = Pointer_stringify(path); - var lookup; try { - lookup = FS.lookupPath(path, { follow: true }); + FS.chdir(path); + return 0; } catch (e) { FS.handleFSError(e); return -1; } - if (!FS.isDir(lookup.node.mode)) { - ___setErrNo(ERRNO_CODES.ENOTDIR); - return -1; - } - var err = FS.nodePermissions(lookup.node, 'x'); - if (err) { - ___setErrNo(err); - return -1; - } - FS.currentPath = lookup.path; - return 0; }, chown__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], chown: function(path, owner, group, dontResolveLastLink) { @@ -909,12 +841,14 @@ LibraryManager.library = { if (size == 0) { ___setErrNo(ERRNO_CODES.EINVAL); return 0; - } else if (size < FS.currentPath.length + 1) { + } + var cwd = FS.cwd(); + if (size < cwd.length + 1) { ___setErrNo(ERRNO_CODES.ERANGE); return 0; } else { - for (var i = 0; i < FS.currentPath.length; i++) { - {{{ makeSetValue('buf', 'i', 'FS.currentPath.charCodeAt(i)', 'i8') }}} + for (var i = 0; i < cwd.length; i++) { + {{{ makeSetValue('buf', 'i', 'cwd.charCodeAt(i)', 'i8') }}} } {{{ makeSetValue('buf', 'i', '0', 'i8') }}} return buf; @@ -1660,12 +1594,6 @@ LibraryManager.library = { __scanString.whiteSpace[{{{ charCode('\v') }}}] = 1; __scanString.whiteSpace[{{{ charCode('\f') }}}] = 1; __scanString.whiteSpace[{{{ charCode('\r') }}}] = 1; - __scanString.whiteSpace[' '] = 1; - __scanString.whiteSpace['\t'] = 1; - __scanString.whiteSpace['\n'] = 1; - __scanString.whiteSpace['\v'] = 1; - __scanString.whiteSpace['\f'] = 1; - __scanString.whiteSpace['\r'] = 1; } // Supports %x, %4x, %d.%d, %lld, %s, %f, %lf. // TODO: Support all format specifiers. @@ -1903,7 +1831,7 @@ LibraryManager.library = { break; } fields++; - } else if (format[formatIndex] in __scanString.whiteSpace) { + } else if (format[formatIndex].charCodeAt(0) in __scanString.whiteSpace) { next = get(); while (next in __scanString.whiteSpace) { if (next <= 0) break mainLoop; // End of input. @@ -1974,6 +1902,7 @@ LibraryManager.library = { var flagLeftAlign = false; var flagAlternative = false; var flagZeroPad = false; + var flagPadSign = false; flagsLoop: while (1) { switch (next) { case {{{ charCode('+') }}}: @@ -1992,6 +1921,9 @@ LibraryManager.library = { flagZeroPad = true; break; } + case {{{ charCode(' ') }}}: + flagPadSign = true; + break; default: break flagsLoop; } @@ -2158,14 +2090,20 @@ LibraryManager.library = { } // Add sign if needed - if (flagAlwaysSigned) { - if (currArg < 0) { - prefix = '-' + prefix; - } else { + if (currArg >= 0) { + if (flagAlwaysSigned) { prefix = '+' + prefix; + } else if (flagPadSign) { + prefix = ' ' + prefix; } } + // Move sign to prefix so we zero-pad after the sign + if (argText.charAt(0) == '-') { + prefix = '-' + prefix; + argText = argText.substr(1); + } + // Add padding. while (prefix.length + argText.length < width) { if (flagLeftAlign) { @@ -2248,8 +2186,12 @@ LibraryManager.library = { if (next == {{{ charCode('E') }}}) argText = argText.toUpperCase(); // Add sign. - if (flagAlwaysSigned && currArg >= 0) { - argText = '+' + argText; + if (currArg >= 0) { + if (flagAlwaysSigned) { + argText = '+' + argText; + } else if (flagPadSign) { + argText = ' ' + argText; + } } } @@ -2887,6 +2829,13 @@ LibraryManager.library = { asprintf: function(s, format, varargs) { return _sprintf(-s, format, varargs); }, + dprintf__deps: ['_formatString', 'write'], + dprintf: function(fd, format, varargs) { + var result = __formatString(format, varargs); + var stack = Runtime.stackSave(); + var ret = _write(fd, allocate(result, 'i8', ALLOC_STACK), result.length); + Runtime.stackRestore(stack); + }, #if TARGET_X86 // va_arg is just like our varargs @@ -2895,6 +2844,7 @@ LibraryManager.library = { vprintf: 'printf', vsprintf: 'sprintf', vasprintf: 'asprintf', + vdprintf: 'dprintf', vscanf: 'scanf', vfscanf: 'fscanf', vsscanf: 'sscanf', @@ -2922,6 +2872,10 @@ LibraryManager.library = { vasprintf: function(s, format, va_arg) { return _asprintf(s, format, {{{ makeGetValue('va_arg', 0, '*') }}}); }, + vdprintf__deps: ['dprintf'], + vdprintf: function (fd, format, va_arg) { + return _dprintf(fd, format, {{{ makeGetValue('va_arg', 0, '*') }}}); + }, vscanf__deps: ['scanf'], vscanf: function(format, va_arg) { return _scanf(format, {{{ makeGetValue('va_arg', 0, '*') }}}); @@ -3054,8 +3008,8 @@ LibraryManager.library = { return ret; }, - abs: 'Math.abs', - labs: 'Math.abs', + abs: 'Math_abs', + labs: 'Math_abs', #if USE_TYPED_ARRAYS == 2 llabs__deps: [function() { Types.preciseI64MathUsed = 1 }], llabs: function(lo, hi) { @@ -3792,6 +3746,7 @@ LibraryManager.library = { }, // We always assume ASCII locale. strcoll: 'strcmp', + strcoll_l: 'strcmp', strcasecmp__asm: true, strcasecmp__sig: 'iii', @@ -3847,8 +3802,8 @@ LibraryManager.library = { p1 = p1|0; p2 = p2|0; num = num|0; var i = 0, v1 = 0, v2 = 0; while ((i|0) < (num|0)) { - var v1 = {{{ makeGetValueAsm('p1', 'i', 'i8', true) }}}; - var v2 = {{{ makeGetValueAsm('p2', 'i', 'i8', true) }}}; + v1 = {{{ makeGetValueAsm('p1', 'i', 'i8', true) }}}; + v2 = {{{ makeGetValueAsm('p2', 'i', 'i8', true) }}}; if ((v1|0) != (v2|0)) return ((v1|0) > (v2|0) ? 1 : -1)|0; i = (i+1)|0; } @@ -4058,6 +4013,7 @@ LibraryManager.library = { } }, _toupper: 'toupper', + toupper_l: 'toupper', tolower__asm: true, tolower__sig: 'ii', @@ -4068,54 +4024,65 @@ LibraryManager.library = { return (chr - {{{ charCode('A') }}} + {{{ charCode('a') }}})|0; }, _tolower: 'tolower', + tolower_l: 'tolower', // The following functions are defined as macros in glibc. islower: function(chr) { return chr >= {{{ charCode('a') }}} && chr <= {{{ charCode('z') }}}; }, + islower_l: 'islower', isupper: function(chr) { return chr >= {{{ charCode('A') }}} && chr <= {{{ charCode('Z') }}}; }, + isupper_l: 'isupper', isalpha: function(chr) { return (chr >= {{{ charCode('a') }}} && chr <= {{{ charCode('z') }}}) || (chr >= {{{ charCode('A') }}} && chr <= {{{ charCode('Z') }}}); }, + isalpha_l: 'isalpha', isdigit: function(chr) { return chr >= {{{ charCode('0') }}} && chr <= {{{ charCode('9') }}}; }, - isdigit_l: 'isdigit', // no locale support yet + isdigit_l: 'isdigit', isxdigit: function(chr) { return (chr >= {{{ charCode('0') }}} && chr <= {{{ charCode('9') }}}) || (chr >= {{{ charCode('a') }}} && chr <= {{{ charCode('f') }}}) || (chr >= {{{ charCode('A') }}} && chr <= {{{ charCode('F') }}}); }, - isxdigit_l: 'isxdigit', // no locale support yet + isxdigit_l: 'isxdigit', isalnum: function(chr) { return (chr >= {{{ charCode('0') }}} && chr <= {{{ charCode('9') }}}) || (chr >= {{{ charCode('a') }}} && chr <= {{{ charCode('z') }}}) || (chr >= {{{ charCode('A') }}} && chr <= {{{ charCode('Z') }}}); }, + isalnum_l: 'isalnum', ispunct: function(chr) { return (chr >= {{{ charCode('!') }}} && chr <= {{{ charCode('/') }}}) || (chr >= {{{ charCode(':') }}} && chr <= {{{ charCode('@') }}}) || (chr >= {{{ charCode('[') }}} && chr <= {{{ charCode('`') }}}) || (chr >= {{{ charCode('{') }}} && chr <= {{{ charCode('~') }}}); }, + ispunct_l: 'ispunct', isspace: function(chr) { return (chr == 32) || (chr >= 9 && chr <= 13); }, + isspace_l: 'isspace', isblank: function(chr) { return chr == {{{ charCode(' ') }}} || chr == {{{ charCode('\t') }}}; }, + isblank_l: 'isblank', iscntrl: function(chr) { return (0 <= chr && chr <= 0x1F) || chr === 0x7F; }, + iscntrl_l: 'iscntrl', isprint: function(chr) { return 0x1F < chr && chr < 0x7F; }, + isprint_l: 'isprint', isgraph: function(chr) { return 0x20 < chr && chr < 0x7F; }, + isgraph_l: 'isgraph', // Lookup tables for glibc ctype implementation. __ctype_b_loc: function() { // http://refspecs.freestandards.org/LSB_3.0.0/LSB-Core-generic/LSB-Core-generic/baselib---ctype-b-loc.html @@ -4216,7 +4183,7 @@ LibraryManager.library = { llvm_va_end: function() {}, llvm_va_copy: function(ppdest, ppsrc) { - // copy the list start + // copy the list start {{{ makeCopyValues('ppdest', 'ppsrc', Runtime.QUANTUM_SIZE, 'null', null, 1) }}}; // copy the list's current offset (will be advanced with each call to va_arg) @@ -4260,9 +4227,9 @@ LibraryManager.library = { var ret = 0; ret = {{{ makeGetValueAsm('ctlz_i8', 'x >>> 24', 'i8') }}}; if ((ret|0) < 8) return ret|0; - var ret = {{{ makeGetValueAsm('ctlz_i8', '(x >> 16)&0xff', 'i8') }}}; + ret = {{{ makeGetValueAsm('ctlz_i8', '(x >> 16)&0xff', 'i8') }}}; if ((ret|0) < 8) return (ret + 8)|0; - var ret = {{{ makeGetValueAsm('ctlz_i8', '(x >> 8)&0xff', 'i8') }}}; + ret = {{{ makeGetValueAsm('ctlz_i8', '(x >> 8)&0xff', 'i8') }}}; if ((ret|0) < 8) return (ret + 16)|0; return ({{{ makeGetValueAsm('ctlz_i8', 'x&0xff', 'i8') }}} + 24)|0; }, @@ -4296,9 +4263,9 @@ LibraryManager.library = { var ret = 0; ret = {{{ makeGetValueAsm('cttz_i8', 'x & 0xff', 'i8') }}}; if ((ret|0) < 8) return ret|0; - var ret = {{{ makeGetValueAsm('cttz_i8', '(x >> 8)&0xff', 'i8') }}}; + ret = {{{ makeGetValueAsm('cttz_i8', '(x >> 8)&0xff', 'i8') }}}; if ((ret|0) < 8) return (ret + 8)|0; - var ret = {{{ makeGetValueAsm('cttz_i8', '(x >> 16)&0xff', 'i8') }}}; + ret = {{{ makeGetValueAsm('cttz_i8', '(x >> 16)&0xff', 'i8') }}}; if ((ret|0) < 8) return (ret + 16)|0; return ({{{ makeGetValueAsm('cttz_i8', 'x >>> 24', 'i8') }}} + 24)|0; }, @@ -4735,30 +4702,30 @@ LibraryManager.library = { // math.h // ========================================================================== - cos: 'Math.cos', - cosf: 'Math.cos', - cosl: 'Math.cos', - sin: 'Math.sin', - sinf: 'Math.sin', - sinl: 'Math.sin', - tan: 'Math.tan', - tanf: 'Math.tan', - tanl: 'Math.tan', - acos: 'Math.acos', - acosf: 'Math.acos', - acosl: 'Math.acos', - asin: 'Math.asin', - asinf: 'Math.asin', - asinl: 'Math.asin', - atan: 'Math.atan', - atanf: 'Math.atan', - atanl: 'Math.atan', - atan2: 'Math.atan2', - atan2f: 'Math.atan2', - atan2l: 'Math.atan2', - exp: 'Math.exp', - expf: 'Math.exp', - expl: 'Math.exp', + cos: 'Math_cos', + cosf: 'Math_cos', + cosl: 'Math_cos', + sin: 'Math_sin', + sinf: 'Math_sin', + sinl: 'Math_sin', + tan: 'Math_tan', + tanf: 'Math_tan', + tanl: 'Math_tan', + acos: 'Math_acos', + acosf: 'Math_acos', + acosl: 'Math_acos', + asin: 'Math_asin', + asinf: 'Math_asin', + asinl: 'Math_asin', + atan: 'Math_atan', + atanf: 'Math_atan', + atanl: 'Math_atan', + atan2: 'Math_atan2', + atan2f: 'Math_atan2', + atan2l: 'Math_atan2', + exp: 'Math_exp', + expf: 'Math_exp', + expl: 'Math_exp', // The erf and erfc functions are inspired from // http://www.digitalmars.com/archives/cplusplus/3634.html @@ -4820,32 +4787,32 @@ LibraryManager.library = { }, erff: 'erf', erfl: 'erf', - log: 'Math.log', - logf: 'Math.log', - logl: 'Math.log', - sqrt: 'Math.sqrt', - sqrtf: 'Math.sqrt', - sqrtl: 'Math.sqrt', - fabs: 'Math.abs', - fabsf: 'Math.abs', - fabsl: 'Math.abs', - ceil: 'Math.ceil', - ceilf: 'Math.ceil', - ceill: 'Math.ceil', - floor: 'Math.floor', - floorf: 'Math.floor', - floorl: 'Math.floor', - pow: 'Math.pow', - powf: 'Math.pow', - powl: 'Math.pow', - llvm_sqrt_f32: 'Math.sqrt', - llvm_sqrt_f64: 'Math.sqrt', - llvm_pow_f32: 'Math.pow', - llvm_pow_f64: 'Math.pow', - llvm_log_f32: 'Math.log', - llvm_log_f64: 'Math.log', - llvm_exp_f32: 'Math.exp', - llvm_exp_f64: 'Math.exp', + log: 'Math_log', + logf: 'Math_log', + logl: 'Math_log', + sqrt: 'Math_sqrt', + sqrtf: 'Math_sqrt', + sqrtl: 'Math_sqrt', + fabs: 'Math_abs', + fabsf: 'Math_abs', + fabsl: 'Math_abs', + ceil: 'Math_ceil', + ceilf: 'Math_ceil', + ceill: 'Math_ceil', + floor: 'Math_floor', + floorf: 'Math_floor', + floorl: 'Math_floor', + pow: 'Math_pow', + powf: 'Math_pow', + powl: 'Math_pow', + llvm_sqrt_f32: 'Math_sqrt', + llvm_sqrt_f64: 'Math_sqrt', + llvm_pow_f32: 'Math_pow', + llvm_pow_f64: 'Math_pow', + llvm_log_f32: 'Math_log', + llvm_log_f64: 'Math_log', + llvm_exp_f32: 'Math_exp', + llvm_exp_f64: 'Math_exp', ldexp: function(x, exp_) { return x * Math.pow(2, exp_); }, @@ -5068,17 +5035,11 @@ LibraryManager.library = { {{{ makeSetValue('cosine', '0', 'cosineVal', 'float') }}}; }, - __div_t_struct_layout: Runtime.generateStructInfo([ - ['i32', 'quot'], - ['i32', 'rem'], - ]), - div__deps: ['__div_t_struct_layout'], div: function(divt, numer, denom) { var quot = Math.floor(numer / denom); var rem = numer - quot * denom; - var offset = ___div_t_struct_layout.rem; - {{{ makeSetValue('divt', '0', 'quot', 'i32') }}}; - {{{ makeSetValue('divt', 'offset', 'rem', 'i32') }}}; + {{{ makeSetValue('divt', C_STRUCTS.div_t.quot, 'quot', 'i32') }}}; + {{{ makeSetValue('divt', C_STRUCTS.div_t.rem, 'rem', 'i32') }}}; return divt; }, @@ -5097,19 +5058,12 @@ LibraryManager.library = { // sys/utsname.h // ========================================================================== - __utsname_struct_layout: Runtime.generateStructInfo([ - ['b65', 'sysname'], - ['b65', 'nodename'], - ['b65', 'release'], - ['b65', 'version'], - ['b65', 'machine'], - ['b65', 'domainname']]), - uname__deps: ['__utsname_struct_layout'], uname: function(name) { // int uname(struct utsname *name); // http://pubs.opengroup.org/onlinepubs/009695399/functions/uname.html + var layout = {{{ JSON.stringify(C_STRUCTS.utsname) }}}; function copyString(element, value) { - var offset = ___utsname_struct_layout[element]; + var offset = layout[element]; for (var i = 0; i < value.length; i++) { {{{ makeSetValue('name', 'offset + i', 'value.charCodeAt(i)', 'i8') }}} } @@ -5405,62 +5359,48 @@ LibraryManager.library = { return time1 - time0; }, - __tm_struct_layout: Runtime.generateStructInfo([ - ['i32', 'tm_sec'], - ['i32', 'tm_min'], - ['i32', 'tm_hour'], - ['i32', 'tm_mday'], - ['i32', 'tm_mon'], - ['i32', 'tm_year'], - ['i32', 'tm_wday'], - ['i32', 'tm_yday'], - ['i32', 'tm_isdst'], - ['i32', 'tm_gmtoff'], - ['i8*', 'tm_zone']]), // Statically allocated time struct. - __tm_current: 'allocate({{{ Runtime.QUANTUM_SIZE }}}*26, "i8", ALLOC_STATIC)', + __tm_current: 'allocate({{{ C_STRUCTS.tm.__size__ }}}, "i8", ALLOC_STATIC)', // Statically allocated timezone string. We only use GMT as a timezone. __tm_timezone: 'allocate(intArrayFromString("GMT"), "i8", ALLOC_STATIC)', // Statically allocated time strings. - __tm_formatted: 'allocate({{{ Runtime.QUANTUM_SIZE }}}*26, "i8", ALLOC_STATIC)', + __tm_formatted: 'allocate({{{ C_STRUCTS.tm.__size__ }}}, "i8", ALLOC_STATIC)', - mktime__deps: ['__tm_struct_layout', 'tzset'], + mktime__deps: ['tzset'], mktime: function(tmPtr) { _tzset(); - var offsets = ___tm_struct_layout; - var year = {{{ makeGetValue('tmPtr', 'offsets.tm_year', 'i32') }}}; + var year = {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_year, 'i32') }}}; var timestamp = new Date(year >= 1900 ? year : year + 1900, - {{{ makeGetValue('tmPtr', 'offsets.tm_mon', 'i32') }}}, - {{{ makeGetValue('tmPtr', 'offsets.tm_mday', 'i32') }}}, - {{{ makeGetValue('tmPtr', 'offsets.tm_hour', 'i32') }}}, - {{{ makeGetValue('tmPtr', 'offsets.tm_min', 'i32') }}}, - {{{ makeGetValue('tmPtr', 'offsets.tm_sec', 'i32') }}}, + {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_mon, 'i32') }}}, + {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_mday, 'i32') }}}, + {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_hour, 'i32') }}}, + {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_min, 'i32') }}}, + {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_sec, 'i32') }}}, 0).getTime() / 1000; - {{{ makeSetValue('tmPtr', 'offsets.tm_wday', 'new Date(timestamp).getDay()', 'i32') }}} + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_wday, 'new Date(timestamp).getDay()', 'i32') }}} var yday = Math.round((timestamp - (new Date(year, 0, 1)).getTime()) / (1000 * 60 * 60 * 24)); - {{{ makeSetValue('tmPtr', 'offsets.tm_yday', 'yday', 'i32') }}} + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_yday, 'yday', 'i32') }}} return timestamp; }, timelocal: 'mktime', - gmtime__deps: ['malloc', '__tm_struct_layout', '__tm_current', 'gmtime_r'], + gmtime__deps: ['malloc', '__tm_current', 'gmtime_r'], gmtime: function(time) { return _gmtime_r(time, ___tm_current); }, - gmtime_r__deps: ['__tm_struct_layout', '__tm_timezone'], + gmtime_r__deps: ['__tm_timezone'], gmtime_r: function(time, tmPtr) { var date = new Date({{{ makeGetValue('time', 0, 'i32') }}}*1000); - var offsets = ___tm_struct_layout; - {{{ makeSetValue('tmPtr', 'offsets.tm_sec', 'date.getUTCSeconds()', 'i32') }}} - {{{ makeSetValue('tmPtr', 'offsets.tm_min', 'date.getUTCMinutes()', 'i32') }}} - {{{ makeSetValue('tmPtr', 'offsets.tm_hour', 'date.getUTCHours()', 'i32') }}} - {{{ makeSetValue('tmPtr', 'offsets.tm_mday', 'date.getUTCDate()', 'i32') }}} - {{{ makeSetValue('tmPtr', 'offsets.tm_mon', 'date.getUTCMonth()', 'i32') }}} - {{{ makeSetValue('tmPtr', 'offsets.tm_year', 'date.getUTCFullYear()-1900', 'i32') }}} - {{{ makeSetValue('tmPtr', 'offsets.tm_wday', 'date.getUTCDay()', 'i32') }}} - {{{ makeSetValue('tmPtr', 'offsets.tm_gmtoff', '0', 'i32') }}} - {{{ makeSetValue('tmPtr', 'offsets.tm_isdst', '0', 'i32') }}} + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_sec, 'date.getUTCSeconds()', 'i32') }}} + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_min, 'date.getUTCMinutes()', 'i32') }}} + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_hour, 'date.getUTCHours()', 'i32') }}} + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_mday, 'date.getUTCDate()', 'i32') }}} + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_mon, 'date.getUTCMonth()', 'i32') }}} + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_year, 'date.getUTCFullYear()-1900', 'i32') }}} + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_wday, 'date.getUTCDay()', 'i32') }}} + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_gmtoff, '0', 'i32') }}} + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_isdst, '0', 'i32') }}} var start = new Date(date); // define date using UTC, start from Jan 01 00:00:00 UTC start.setUTCDate(1); start.setUTCMonth(0); @@ -5469,8 +5409,8 @@ LibraryManager.library = { start.setUTCSeconds(0); start.setUTCMilliseconds(0); var yday = Math.floor((date.getTime() - start.getTime()) / (1000 * 60 * 60 * 24)); - {{{ makeSetValue('tmPtr', 'offsets.tm_yday', 'yday', 'i32') }}} - {{{ makeSetValue('tmPtr', 'offsets.tm_zone', '___tm_timezone', 'i32') }}} + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_yday, 'yday', 'i32') }}} + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_zone, '___tm_timezone', 'i32') }}} return tmPtr; }, @@ -5484,33 +5424,32 @@ LibraryManager.library = { return ret; }, - localtime__deps: ['malloc', '__tm_struct_layout', '__tm_current', 'localtime_r'], + localtime__deps: ['malloc', '__tm_current', 'localtime_r'], localtime: function(time) { return _localtime_r(time, ___tm_current); }, - localtime_r__deps: ['__tm_struct_layout', '__tm_timezone', 'tzset'], + localtime_r__deps: ['__tm_timezone', 'tzset'], localtime_r: function(time, tmPtr) { _tzset(); - var offsets = ___tm_struct_layout; var date = new Date({{{ makeGetValue('time', 0, 'i32') }}}*1000); - {{{ makeSetValue('tmPtr', 'offsets.tm_sec', 'date.getSeconds()', 'i32') }}} - {{{ makeSetValue('tmPtr', 'offsets.tm_min', 'date.getMinutes()', 'i32') }}} - {{{ makeSetValue('tmPtr', 'offsets.tm_hour', 'date.getHours()', 'i32') }}} - {{{ makeSetValue('tmPtr', 'offsets.tm_mday', 'date.getDate()', 'i32') }}} - {{{ makeSetValue('tmPtr', 'offsets.tm_mon', 'date.getMonth()', 'i32') }}} - {{{ makeSetValue('tmPtr', 'offsets.tm_year', 'date.getFullYear()-1900', 'i32') }}} - {{{ makeSetValue('tmPtr', 'offsets.tm_wday', 'date.getDay()', 'i32') }}} + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_sec, 'date.getSeconds()', 'i32') }}} + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_min, 'date.getMinutes()', 'i32') }}} + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_hour, 'date.getHours()', 'i32') }}} + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_mday, 'date.getDate()', 'i32') }}} + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_mon, 'date.getMonth()', 'i32') }}} + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_year, 'date.getFullYear()-1900', 'i32') }}} + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_wday, 'date.getDay()', 'i32') }}} var start = new Date(date.getFullYear(), 0, 1); var yday = Math.floor((date.getTime() - start.getTime()) / (1000 * 60 * 60 * 24)); - {{{ makeSetValue('tmPtr', 'offsets.tm_yday', 'yday', 'i32') }}} - {{{ makeSetValue('tmPtr', 'offsets.tm_gmtoff', 'start.getTimezoneOffset() * 60', 'i32') }}} + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_yday, 'yday', 'i32') }}} + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_gmtoff, 'start.getTimezoneOffset() * 60', 'i32') }}} var dst = Number(start.getTimezoneOffset() != date.getTimezoneOffset()); - {{{ makeSetValue('tmPtr', 'offsets.tm_isdst', 'dst', 'i32') }}} + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_isdst, 'dst', 'i32') }}} - {{{ makeSetValue('tmPtr', 'offsets.tm_zone', '___tm_timezone', 'i32') }}} + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_zone, '___tm_timezone', 'i32') }}} return tmPtr; }, @@ -5621,21 +5560,21 @@ LibraryManager.library = { return newDate; }, - strftime__deps: ['__tm_struct_layout', '_isLeapYear', '_arraySum', '_addDays', '_MONTH_DAYS_REGULAR', '_MONTH_DAYS_LEAP'], + strftime__deps: ['_isLeapYear', '_arraySum', '_addDays', '_MONTH_DAYS_REGULAR', '_MONTH_DAYS_LEAP'], strftime: function(s, maxsize, format, tm) { // size_t strftime(char *restrict s, size_t maxsize, const char *restrict format, const struct tm *restrict timeptr); // http://pubs.opengroup.org/onlinepubs/009695399/functions/strftime.html var date = { - tm_sec: {{{ makeGetValue('tm', '___tm_struct_layout.tm_sec', 'i32') }}}, - tm_min: {{{ makeGetValue('tm', '___tm_struct_layout.tm_min', 'i32') }}}, - tm_hour: {{{ makeGetValue('tm', '___tm_struct_layout.tm_hour', 'i32') }}}, - tm_mday: {{{ makeGetValue('tm', '___tm_struct_layout.tm_mday', 'i32') }}}, - tm_mon: {{{ makeGetValue('tm', '___tm_struct_layout.tm_mon', 'i32') }}}, - tm_year: {{{ makeGetValue('tm', '___tm_struct_layout.tm_year', 'i32') }}}, - tm_wday: {{{ makeGetValue('tm', '___tm_struct_layout.tm_wday', 'i32') }}}, - tm_yday: {{{ makeGetValue('tm', '___tm_struct_layout.tm_yday', 'i32') }}}, - tm_isdst: {{{ makeGetValue('tm', '___tm_struct_layout.tm_isdst', 'i32') }}} + tm_sec: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_sec, 'i32') }}}, + tm_min: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_min, 'i32') }}}, + tm_hour: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_hour, 'i32') }}}, + tm_mday: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_mday, 'i32') }}}, + tm_mon: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_mon, 'i32') }}}, + tm_year: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_year, 'i32') }}}, + tm_wday: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_wday, 'i32') }}}, + tm_yday: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_yday, 'i32') }}}, + tm_isdst: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_isdst, 'i32') }}} }; var pattern = Pointer_stringify(format); @@ -5918,7 +5857,7 @@ LibraryManager.library = { }, strftime_l: 'strftime', // no locale support yet - strptime__deps: ['__tm_struct_layout', '_isLeapYear', '_arraySum', '_addDays', '_MONTH_DAYS_REGULAR', '_MONTH_DAYS_LEAP'], + strptime__deps: ['_isLeapYear', '_arraySum', '_addDays', '_MONTH_DAYS_REGULAR', '_MONTH_DAYS_LEAP'], strptime: function(buf, format, tm) { // char *strptime(const char *restrict buf, const char *restrict format, struct tm *restrict tm); // http://pubs.opengroup.org/onlinepubs/009695399/functions/strptime.html @@ -5996,12 +5935,12 @@ LibraryManager.library = { return (typeof value !== 'number' || isNaN(value)) ? min : (value>=min ? (value<=max ? value: max): min); }; return { - year: fixup({{{ makeGetValue('tm', '___tm_struct_layout.tm_year', 'i32', 0, 0, 1) }}} + 1900 , 1970, 9999), - month: fixup({{{ makeGetValue('tm', '___tm_struct_layout.tm_mon', 'i32', 0, 0, 1) }}}, 0, 11), - day: fixup({{{ makeGetValue('tm', '___tm_struct_layout.tm_mday', 'i32', 0, 0, 1) }}}, 1, 31), - hour: fixup({{{ makeGetValue('tm', '___tm_struct_layout.tm_hour', 'i32', 0, 0, 1) }}}, 0, 23), - min: fixup({{{ makeGetValue('tm', '___tm_struct_layout.tm_min', 'i32', 0, 0, 1) }}}, 0, 59), - sec: fixup({{{ makeGetValue('tm', '___tm_struct_layout.tm_sec', 'i32', 0, 0, 1) }}}, 0, 59) + year: fixup({{{ makeGetValue('tm', C_STRUCTS.tm.tm_year, 'i32', 0, 0, 1) }}} + 1900 , 1970, 9999), + month: fixup({{{ makeGetValue('tm', C_STRUCTS.tm.tm_mon, 'i32', 0, 0, 1) }}}, 0, 11), + day: fixup({{{ makeGetValue('tm', C_STRUCTS.tm.tm_mday, 'i32', 0, 0, 1) }}}, 1, 31), + hour: fixup({{{ makeGetValue('tm', C_STRUCTS.tm.tm_hour, 'i32', 0, 0, 1) }}}, 0, 23), + min: fixup({{{ makeGetValue('tm', C_STRUCTS.tm.tm_min, 'i32', 0, 0, 1) }}}, 0, 59), + sec: fixup({{{ makeGetValue('tm', C_STRUCTS.tm.tm_sec, 'i32', 0, 0, 1) }}}, 0, 59) }; }; @@ -6141,15 +6080,15 @@ LibraryManager.library = { */ var fullDate = new Date(date.year, date.month, date.day, date.hour, date.min, date.sec, 0); - {{{ makeSetValue('tm', '___tm_struct_layout.tm_sec', 'fullDate.getSeconds()', 'i32') }}} - {{{ makeSetValue('tm', '___tm_struct_layout.tm_min', 'fullDate.getMinutes()', 'i32') }}} - {{{ makeSetValue('tm', '___tm_struct_layout.tm_hour', 'fullDate.getHours()', 'i32') }}} - {{{ makeSetValue('tm', '___tm_struct_layout.tm_mday', 'fullDate.getDate()', 'i32') }}} - {{{ makeSetValue('tm', '___tm_struct_layout.tm_mon', 'fullDate.getMonth()', 'i32') }}} - {{{ makeSetValue('tm', '___tm_struct_layout.tm_year', 'fullDate.getFullYear()-1900', 'i32') }}} - {{{ makeSetValue('tm', '___tm_struct_layout.tm_wday', 'fullDate.getDay()', 'i32') }}} - {{{ makeSetValue('tm', '___tm_struct_layout.tm_yday', '__arraySum(__isLeapYear(fullDate.getFullYear()) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, fullDate.getMonth()-1)+fullDate.getDate()-1', 'i32') }}} - {{{ makeSetValue('tm', '___tm_struct_layout.tm_isdst', '0', 'i32') }}} + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_sec, 'fullDate.getSeconds()', 'i32') }}} + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_min, 'fullDate.getMinutes()', 'i32') }}} + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_hour, 'fullDate.getHours()', 'i32') }}} + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_mday, 'fullDate.getDate()', 'i32') }}} + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_mon, 'fullDate.getMonth()', 'i32') }}} + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_year, 'fullDate.getFullYear()-1900', 'i32') }}} + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_wday, 'fullDate.getDay()', 'i32') }}} + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_yday, '__arraySum(__isLeapYear(fullDate.getFullYear()) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, fullDate.getMonth()-1)+fullDate.getDate()-1', 'i32') }}} + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_isdst, '0', 'i32') }}} // we need to convert the matched sequence into an integer array to take care of UTF-8 characters > 0x7F // TODO: not sure that intArrayFromString handles all unicode characters correctly @@ -6174,25 +6113,21 @@ LibraryManager.library = { // sys/time.h // ========================================================================== - __timespec_struct_layout: Runtime.generateStructInfo([ - ['i32', 'tv_sec'], - ['i32', 'tv_nsec']]), - nanosleep__deps: ['usleep', '__timespec_struct_layout'], + nanosleep__deps: ['usleep'], nanosleep: function(rqtp, rmtp) { // int nanosleep(const struct timespec *rqtp, struct timespec *rmtp); - var seconds = {{{ makeGetValue('rqtp', '___timespec_struct_layout.tv_sec', 'i32') }}}; - var nanoseconds = {{{ makeGetValue('rqtp', '___timespec_struct_layout.tv_nsec', 'i32') }}}; - {{{ makeSetValue('rmtp', '___timespec_struct_layout.tv_sec', '0', 'i32') }}} - {{{ makeSetValue('rmtp', '___timespec_struct_layout.tv_nsec', '0', 'i32') }}} + var seconds = {{{ makeGetValue('rqtp', C_STRUCTS.timespec.tv_sec, 'i32') }}}; + var nanoseconds = {{{ makeGetValue('rqtp', C_STRUCTS.timespec.tv_nsec, 'i32') }}}; + {{{ makeSetValue('rmtp', C_STRUCTS.timespec.tv_sec, '0', 'i32') }}} + {{{ makeSetValue('rmtp', C_STRUCTS.timespec.tv_nsec, '0', 'i32') }}} return _usleep((seconds * 1e6) + (nanoseconds / 1000)); }, // TODO: Implement these for real. - clock_gettime__deps: ['__timespec_struct_layout'], clock_gettime: function(clk_id, tp) { // int clock_gettime(clockid_t clk_id, struct timespec *tp); var now = Date.now(); - {{{ makeSetValue('tp', '___timespec_struct_layout.tv_sec', 'Math.floor(now/1000)', 'i32') }}}; // seconds - {{{ makeSetValue('tp', '___timespec_struct_layout.tv_nsec', '(now % 1000) * 1000 * 1000', 'i32') }}}; // nanoseconds (really milliseconds) + {{{ makeSetValue('tp', C_STRUCTS.timespec.tv_sec, 'Math.floor(now/1000)', 'i32') }}}; // seconds + {{{ makeSetValue('tp', C_STRUCTS.timespec.tv_nsec, '(now % 1000) * 1000 * 1000', 'i32') }}}; // nanoseconds (really milliseconds) return 0; }, clock_settime: function(clk_id, tp) { @@ -6200,41 +6135,31 @@ LibraryManager.library = { // Nothing. return 0; }, - clock_getres__deps: ['__timespec_struct_layout'], clock_getres: function(clk_id, res) { // int clock_getres(clockid_t clk_id, struct timespec *res); - {{{ makeSetValue('res', '___timespec_struct_layout.tv_sec', '1', 'i32') }}} - {{{ makeSetValue('res', '___timespec_struct_layout.tv_nsec', '1000 * 1000', 'i32') }}} // resolution is milliseconds + {{{ makeSetValue('res', C_STRUCTS.timespec.tv_sec, '1', 'i32') }}} + {{{ makeSetValue('res', C_STRUCTS.timespec.tv_nsec, '1000 * 1000', 'i32') }}} // resolution is milliseconds return 0; }, // http://pubs.opengroup.org/onlinepubs/000095399/basedefs/sys/time.h.html gettimeofday: function(ptr) { - // %struct.timeval = type { i32, i32 } - {{{ (LibraryManager.structs.gettimeofday = Runtime.calculateStructAlignment({ fields: ['i32', 'i32'] }), null) }}} var now = Date.now(); - {{{ makeSetValue('ptr', LibraryManager.structs.gettimeofday[0], 'Math.floor(now/1000)', 'i32') }}}; // seconds - {{{ makeSetValue('ptr', LibraryManager.structs.gettimeofday[1], 'Math.floor((now-1000*Math.floor(now/1000))*1000)', 'i32') }}}; // microseconds + {{{ makeSetValue('ptr', C_STRUCTS.timeval.tv_sec, 'Math.floor(now/1000)', 'i32') }}}; // seconds + {{{ makeSetValue('ptr', C_STRUCTS.timeval.tv_usec, 'Math.floor((now-1000*Math.floor(now/1000))*1000)', 'i32') }}}; // microseconds return 0; }, // ========================================================================== // sys/timeb.h // ========================================================================== - - __timeb_struct_layout: Runtime.generateStructInfo([ - ['i32', 'time'], - ['i16', 'millitm'], - ['i16', 'timezone'], - ['i16', 'dstflag'] - ]), - ftime__deps: ['__timeb_struct_layout'], + ftime: function(p) { var millis = Date.now(); - {{{ makeSetValue('p', '___timeb_struct_layout.time', 'Math.floor(millis/1000)', 'i32') }}}; - {{{ makeSetValue('p', '___timeb_struct_layout.millitm', 'millis % 1000', 'i16') }}}; - {{{ makeSetValue('p', '___timeb_struct_layout.timezone', '0', 'i16') }}}; // TODO - {{{ makeSetValue('p', '___timeb_struct_layout.dstflag', '0', 'i16') }}}; // TODO + {{{ makeSetValue('p', C_STRUCTS.timeb.time, 'Math.floor(millis/1000)', 'i32') }}}; + {{{ makeSetValue('p', C_STRUCTS.timeb.millitm, 'millis % 1000', 'i16') }}}; + {{{ makeSetValue('p', C_STRUCTS.timeb.timezone, '0', 'i16') }}}; // TODO + {{{ makeSetValue('p', C_STRUCTS.timeb.dstflag, '0', 'i16') }}}; // TODO return 0; }, @@ -6242,18 +6167,13 @@ LibraryManager.library = { // sys/times.h // ========================================================================== - __tms_struct_layout: Runtime.generateStructInfo([ - ['i32', 'tms_utime'], - ['i32', 'tms_stime'], - ['i32', 'tms_cutime'], - ['i32', 'tms_cstime']]), - times__deps: ['__tms_struct_layout', 'memset'], + times__deps: ['memset'], times: function(buffer) { // clock_t times(struct tms *buffer); // http://pubs.opengroup.org/onlinepubs/009695399/functions/times.html // NOTE: This is fake, since we can't calculate real CPU time usage in JS. if (buffer !== 0) { - _memset(buffer, 0, ___tms_struct_layout.__size__); + _memset(buffer, 0, {{{ C_STRUCTS.tms.__size__ }}}); } return 0; }, @@ -6435,11 +6355,15 @@ LibraryManager.library = { // locale.h // ========================================================================== + newlocale__deps: ['malloc'], newlocale: function(mask, locale, base) { - return 0; + return _malloc({{{ QUANTUM_SIZE}}}); }, - freelocale: function(locale) {}, + freelocale__deps: ['free'], + freelocale: function(locale) { + _free(locale); + }, uselocale: function(locale) { return 0; @@ -6911,14 +6835,10 @@ LibraryManager.library = { // ========================================================================== // TODO: Implement for real. - __rlimit_struct_layout: Runtime.generateStructInfo([ - ['i32', 'rlim_cur'], - ['i32', 'rlim_max']]), - getrlimit__deps: ['__rlimit_struct_layout'], getrlimit: function(resource, rlp) { // int getrlimit(int resource, struct rlimit *rlp); - {{{ makeSetValue('rlp', '___rlimit_struct_layout.rlim_cur', '-1', 'i32') }}} // RLIM_INFINITY - {{{ makeSetValue('rlp', '___rlimit_struct_layout.rlim_max', '-1', 'i32') }}} // RLIM_INFINITY + {{{ makeSetValue('rlp', C_STRUCTS.rlimit.rlim_cur, '-1', 'i32') }}} // RLIM_INFINITY + {{{ makeSetValue('rlp', C_STRUCTS.rlimit.rlim_max, '-1', 'i32') }}} // RLIM_INFINITY return 0; }, setrlimit: function(resource, rlp) { @@ -6928,33 +6848,12 @@ LibraryManager.library = { __01getrlimit64_: 'getrlimit', // TODO: Implement for real. We just do time used, and no useful data - __rusage_struct_layout: Runtime.generateStructInfo([ - ['i64', 'ru_utime'], - ['i64', 'ru_stime'], - ['i32', 'ru_maxrss'], - ['i32', 'ru_ixrss'], - ['i32', 'ru_idrss'], - ['i32', 'ru_isrss'], - ['i32', 'ru_minflt'], - ['i32', 'ru_majflt'], - ['i32', 'ru_nswap'], - ['i32', 'ru_inblock'], - ['i32', 'ru_oublock'], - ['i32', 'ru_msgsnd'], - ['i32', 'ru_msgrcv'], - ['i32', 'ru_nsignals'], - ['i32', 'ru_nvcsw'], - ['i32', 'ru_nivcsw']]), - getrusage__deps: ['__rusage_struct_layout'], getrusage: function(resource, rlp) { - // %struct.timeval = type { i32, i32 } - var timeval = Runtime.calculateStructAlignment({ fields: ['i32', 'i32'] }); - // int getrusage(int resource, struct rusage *rlp); - {{{ makeSetValue('rlp', '___rusage_struct_layout.ru_utime+timeval[0]', '1', 'i32') }}} - {{{ makeSetValue('rlp', '___rusage_struct_layout.ru_utime+timeval[1]', '2', 'i32') }}} - {{{ makeSetValue('rlp', '___rusage_struct_layout.ru_stime+timeval[0]', '3', 'i32') }}} - {{{ makeSetValue('rlp', '___rusage_struct_layout.ru_stime+timeval[1]', '4', 'i32') }}} + {{{ makeSetValue('rlp', C_STRUCTS.rusage.ru_utime.tv_sec, '1', 'i32') }}} + {{{ makeSetValue('rlp', C_STRUCTS.rusage.ru_utime.tv_usec, '2', 'i32') }}} + {{{ makeSetValue('rlp', C_STRUCTS.rusage.ru_stime.tv_sec, '3', 'i32') }}} + {{{ makeSetValue('rlp', C_STRUCTS.rusage.ru_stime.tv_usec, '4', 'i32') }}} return 0; }, @@ -7462,25 +7361,6 @@ LibraryManager.library = { }, // note: lots of leaking here! - __hostent_struct_layout: Runtime.generateStructInfo([ - ['i8*', 'h_name'], - ['i8**', 'h_aliases'], - ['i32', 'h_addrtype'], - ['i32', 'h_length'], - ['i8**', 'h_addr_list'], - ]), - - _addrinfo_layout: Runtime.generateStructInfo([ - ['i32', 'ai_flags'], - ['i32', 'ai_family'], - ['i32', 'ai_socktype'], - ['i32', 'ai_protocol'], - ['i32', 'ai_addrlen'], - ['*', 'ai_addr'], - ['*', 'ai_canonname'], - ['*', 'ai_next'] - ]), - gethostbyaddr__deps: ['$DNS', 'gethostbyname', '_inet_ntop4_raw'], gethostbyaddr: function (addr, addrlen, type) { if (type !== {{{ cDefine('AF_INET') }}}) { @@ -7497,40 +7377,40 @@ LibraryManager.library = { return _gethostbyname(hostp); }, - gethostbyname__deps: ['$DNS', '__hostent_struct_layout', '_inet_pton4_raw'], + gethostbyname__deps: ['$DNS', '_inet_pton4_raw'], gethostbyname: function(name) { name = Pointer_stringify(name); // generate hostent - var ret = _malloc(___hostent_struct_layout.__size__); // XXX possibly leaked, as are others here + var ret = _malloc({{{ C_STRUCTS.hostent.__size__ }}}); // XXX possibly leaked, as are others here var nameBuf = _malloc(name.length+1); writeStringToMemory(name, nameBuf); - {{{ makeSetValue('ret', '___hostent_struct_layout.h_name', 'nameBuf', 'i8*') }}} + {{{ makeSetValue('ret', C_STRUCTS.hostent.h_name, 'nameBuf', 'i8*') }}} var aliasesBuf = _malloc(4); {{{ makeSetValue('aliasesBuf', '0', '0', 'i8*') }}} - {{{ makeSetValue('ret', '___hostent_struct_layout.h_aliases', 'aliasesBuf', 'i8**') }}} + {{{ makeSetValue('ret', C_STRUCTS.hostent.h_aliases, 'aliasesBuf', 'i8**') }}} var afinet = {{{ cDefine('AF_INET') }}}; - {{{ makeSetValue('ret', '___hostent_struct_layout.h_addrtype', 'afinet', 'i32') }}} - {{{ makeSetValue('ret', '___hostent_struct_layout.h_length', '4', 'i32') }}} + {{{ makeSetValue('ret', C_STRUCTS.hostent.h_addrtype, 'afinet', 'i32') }}} + {{{ makeSetValue('ret', C_STRUCTS.hostent.h_length, '4', 'i32') }}} var addrListBuf = _malloc(12); {{{ makeSetValue('addrListBuf', '0', 'addrListBuf+8', 'i32*') }}} {{{ makeSetValue('addrListBuf', '4', '0', 'i32*') }}} {{{ makeSetValue('addrListBuf', '8', '__inet_pton4_raw(DNS.lookup_name(name))', 'i32') }}} - {{{ makeSetValue('ret', '___hostent_struct_layout.h_addr_list', 'addrListBuf', 'i8**') }}} + {{{ makeSetValue('ret', C_STRUCTS.hostent.h_addr_list, 'addrListBuf', 'i8**') }}} return ret; }, gethostbyname_r__deps: ['gethostbyname'], gethostbyname_r: function(name, ret, buf, buflen, out, err) { var data = _gethostbyname(name); - _memcpy(ret, data, ___hostent_struct_layout.__size__); + _memcpy(ret, data, {{{ C_STRUCTS.hostent.__size__ }}}); _free(data); {{{ makeSetValue('err', '0', '0', 'i32') }}}; {{{ makeSetValue('out', '0', 'ret', '*') }}}; return 0; }, - getaddrinfo__deps: ['$Sockets', '$DNS', '_addrinfo_layout', '_inet_pton4_raw', '_inet_ntop4_raw', '_inet_pton6_raw', '_inet_ntop6_raw', '_write_sockaddr', 'htonl'], + getaddrinfo__deps: ['$Sockets', '$DNS', '_inet_pton4_raw', '_inet_ntop4_raw', '_inet_pton6_raw', '_inet_ntop6_raw', '_write_sockaddr', 'htonl'], getaddrinfo: function(node, service, hint, out) { var addrs = []; var canon = null; @@ -7547,8 +7427,8 @@ LibraryManager.library = { var res; salen = family === {{{ cDefine('AF_INET6') }}} ? - Sockets.sockaddr_in6_layout.__size__ : - Sockets.sockaddr_in_layout.__size__; + {{{ C_STRUCTS.sockaddr_in6.__size__ }}} : + {{{ C_STRUCTS.sockaddr_in.__size__ }}}; addr = family === {{{ cDefine('AF_INET6') }}} ? __inet_ntop6_raw(addr) : __inet_ntop4_raw(addr); @@ -7556,28 +7436,28 @@ LibraryManager.library = { res = __write_sockaddr(sa, family, addr, port); assert(!res.errno); - ai = _malloc(__addrinfo_layout.__size__); - {{{ makeSetValue('ai', '__addrinfo_layout.ai_family', 'family', 'i32') }}}; - {{{ makeSetValue('ai', '__addrinfo_layout.ai_socktype', 'type', 'i32') }}}; - {{{ makeSetValue('ai', '__addrinfo_layout.ai_protocol', 'proto', 'i32') }}}; + ai = _malloc({{{ C_STRUCTS.addrinfo.__size__ }}}); + {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_family, 'family', 'i32') }}}; + {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_socktype, 'type', 'i32') }}}; + {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_protocol, 'proto', 'i32') }}}; if (canon) { - {{{ makeSetValue('ai', '__addrinfo_layout.ai_canonname', 'canon', 'i32') }}}; + {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_canonname, 'canon', 'i32') }}}; } - {{{ makeSetValue('ai', '__addrinfo_layout.ai_addr', 'sa', '*') }}}; + {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_addr, 'sa', '*') }}}; if (family === {{{ cDefine('AF_INET6') }}}) { - {{{ makeSetValue('ai', '__addrinfo_layout.ai_addrlen', 'Sockets.sockaddr_in6_layout.__size__', 'i32') }}}; + {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_addrlen, C_STRUCTS.sockaddr_in6.__size__, 'i32') }}}; } else { - {{{ makeSetValue('ai', '__addrinfo_layout.ai_addrlen', 'Sockets.sockaddr_in_layout.__size__', 'i32') }}}; + {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_addrlen, C_STRUCTS.sockaddr_in.__size__, 'i32') }}}; } return ai; } if (hint) { - flags = {{{ makeGetValue('hint', '__addrinfo_layout.ai_flags', 'i32') }}}; - family = {{{ makeGetValue('hint', '__addrinfo_layout.ai_family', 'i32') }}}; - type = {{{ makeGetValue('hint', '__addrinfo_layout.ai_socktype', 'i32') }}}; - proto = {{{ makeGetValue('hint', '__addrinfo_layout.ai_protocol', 'i32') }}}; + flags = {{{ makeGetValue('hint', C_STRUCTS.addrinfo.ai_flags, 'i32') }}}; + family = {{{ makeGetValue('hint', C_STRUCTS.addrinfo.ai_family, 'i32') }}}; + type = {{{ makeGetValue('hint', C_STRUCTS.addrinfo.ai_socktype, 'i32') }}}; + proto = {{{ makeGetValue('hint', C_STRUCTS.addrinfo.ai_protocol, 'i32') }}}; } if (type && !proto) { proto = type === {{{ cDefine('SOCK_DGRAM') }}} ? {{{ cDefine('IPPROTO_UDP') }}} : {{{ cDefine('IPPROTO_TCP') }}}; @@ -7593,7 +7473,7 @@ LibraryManager.library = { {{{ cDefine('AI_NUMERICSERV') }}}|{{{ cDefine('AI_V4MAPPED') }}}|{{{ cDefine('AI_ALL') }}}|{{{ cDefine('AI_ADDRCONFIG') }}})) { return {{{ cDefine('EAI_BADFLAGS') }}}; } - if (({{{ makeGetValue('hint', '__addrinfo_layout.ai_flags', 'i32') }}} & {{{ cDefine('AI_CANONNAME') }}}) && !node) { + if (({{{ makeGetValue('hint', C_STRUCTS.addrinfo.ai_flags, 'i32') }}} & {{{ cDefine('AI_CANONNAME') }}}) && !node) { return {{{ cDefine('EAI_BADFLAGS') }}}; } if (flags & {{{ cDefine('AI_ADDRCONFIG') }}}) { @@ -7689,14 +7569,14 @@ LibraryManager.library = { return 0; }, - freeaddrinfo__deps: ['$Sockets', '_addrinfo_layout'], + freeaddrinfo__deps: ['$Sockets'], freeaddrinfo: function(ai) { - var sa = {{{ makeGetValue('ai', '__addrinfo_layout.ai_addr', '*') }}}; + var sa = {{{ makeGetValue('ai', C_STRUCTS.addrinfo.ai_addr, '*') }}}; _free(sa); _free(ai); }, - getnameinfo__deps: ['$Sockets', '$DNS', '__hostent_struct_layout', '_read_sockaddr'], + getnameinfo__deps: ['$Sockets', '$DNS', '_read_sockaddr'], getnameinfo: function (sa, salen, node, nodelen, serv, servlen, flags) { var info = __read_sockaddr(sa, salen); if (info.errno) { @@ -7763,33 +7643,7 @@ LibraryManager.library = { localAddr: 0xfe00000a, // Local address is always 10.0.0.254 addrPool: [ 0x0200000a, 0x0300000a, 0x0400000a, 0x0500000a, 0x0600000a, 0x0700000a, 0x0800000a, 0x0900000a, 0x0a00000a, - 0x0b00000a, 0x0c00000a, 0x0d00000a, 0x0e00000a], /* 0x0100000a is reserved */ - sockaddr_in_layout: Runtime.generateStructInfo([ - ['i16', 'sin_family'], - ['i16', 'sin_port'], - ['i32', 'sin_addr'], - ['b8', 'sin_zero'], - ]), - sockaddr_in6_layout: Runtime.generateStructInfo([ - ['i16', 'sin6_family'], - ['i16', 'sin6_port'], - ['i32', 'sin6_flowinfo'], - ['b16', 'sin6_addr'], - ['i32', 'sin6_scope_id'] - ]), - msghdr_layout: Runtime.generateStructInfo([ - ['*', 'msg_name'], - ['i32', 'msg_namelen'], - ['*', 'msg_iov'], - ['i32', 'msg_iovlen'], - ['*', 'msg_control'], - ['i32', 'msg_controllen'], - ['i32', 'msg_flags'], - ]), - iovec_layout: Runtime.generateStructInfo([ - ['i8*', 'iov_base'], - ['i32', 'iov_len'] - ]) + 0x0b00000a, 0x0c00000a, 0x0d00000a, 0x0e00000a] /* 0x0100000a is reserved */ }, #if SOCKET_WEBRTC @@ -7953,8 +7807,8 @@ LibraryManager.library = { var info = FS.getStream(fd); if (!info) return -1; if (addr) { - info.port = _ntohs(getValue(addr + Sockets.sockaddr_in_layout.sin_port, 'i16')); - // info.addr = getValue(addr + Sockets.sockaddr_in_layout.sin_addr, 'i32'); + info.port = _ntohs(getValue(addr + {{{ C_STRUCTS.sockaddr_in.sin_port }}}, 'i16')); + // info.addr = getValue(addr + {{{ C_STRUCTS.sockaddr_in.sin_addr.s_addr }}}, 'i32'); } if (!info.port) { info.port = _mkport(); @@ -7978,10 +7832,10 @@ LibraryManager.library = { _bind(fd); } - var name = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_name', '*') }}}; + var name = {{{ makeGetValue('msg', C_STRUCTS.msghdr.msg_name, '*') }}}; assert(name, 'sendmsg on non-connected socket, and no name/address in the message'); - var port = _ntohs(getValue(name + Sockets.sockaddr_in_layout.sin_port, 'i16')); - var addr = getValue(name + Sockets.sockaddr_in_layout.sin_addr, 'i32'); + var port = _ntohs(getValue(name + {{{ C_STRUCTS.sockaddr_in.sin_port }}}, 'i16')); + var addr = getValue(name + {{{ C_STRUCTS.sockaddr_in.sin_addr.s_addr }}}, 'i32'); var connection = Sockets.connections[addr]; // var host = __inet_ntop4_raw(addr); @@ -7990,8 +7844,8 @@ LibraryManager.library = { return -1; } - var iov = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iov', 'i8*') }}}; - var num = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iovlen', 'i32') }}}; + var iov = {{{ makeGetValue('msg', C_STRUCTS.msghdr.msg_iov, 'i8*') }}}; + var num = {{{ makeGetValue('msg', C_STRUCTS.msghdr.msg_iovlen, 'i32') }}}; #if SOCKET_DEBUG Module.print('sendmsg vecs: ' + num); #endif @@ -8052,13 +7906,13 @@ LibraryManager.library = { Module.print('recvmsg bytes: ' + bytes + ' | ' + Array.prototype.slice.call(buffer)); #endif // write source - var name = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_name', '*') }}}; - {{{ makeSetValue('name', 'Sockets.sockaddr_in_layout.sin_addr', 'addr', 'i32') }}}; - {{{ makeSetValue('name', 'Sockets.sockaddr_in_layout.sin_port', '_htons(header[0])', 'i16') }}}; + var name = {{{ makeGetValue('msg', C_STRUCTS.msghdr.msg_name, '*') }}}; + {{{ makeSetValue('name', C_STRUCTS.sockaddr_in.sin_addr.s_addr, 'addr', 'i32') }}}; + {{{ makeSetValue('name', C_STRUCTS.sockaddr_in.sin_port, '_htons(header[0])', 'i16') }}}; // write data var ret = bytes; - var iov = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iov', 'i8*') }}}; - var num = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iovlen', 'i32') }}}; + var iov = {{{ makeGetValue('msg', C_STRUCTS.msghdr.msg_iov, 'i8*') }}}; + var num = {{{ makeGetValue('msg', C_STRUCTS.msghdr.msg_iovlen, 'i32') }}}; var bufferPos = 0; for (var i = 0; i < num && bytes > 0; i++) { var currNum = {{{ makeGetValue('iov', '8*i + 4', 'i32') }}}; @@ -8112,9 +7966,9 @@ LibraryManager.library = { var info = FS.getStream(fd); if (!info) return -1; if (addr) { - setValue(addr + Sockets.sockaddr_in_layout.sin_addr, info.addr, 'i32'); - setValue(addr + Sockets.sockaddr_in_layout.sin_port, info.port, 'i32'); - setValue(addrlen, Sockets.sockaddr_in_layout.__size__, 'i32'); + setValue(addr + {{{ C_STRUCTS.sockaddr_in.sin_addr.s_addr }}}, info.addr, 'i32'); + setValue(addr + {{{ C_STRUCTS.sockaddr_in.sin_port }}}, info.port, 'i32'); + setValue(addrlen, {{{ C_STRUCTS.sockaddr_in.__size__ }}}, 'i32'); } return fd; }, @@ -8180,27 +8034,27 @@ LibraryManager.library = { _read_sockaddr__deps: ['$Sockets', '_inet_ntop4_raw', '_inet_ntop6_raw'], _read_sockaddr: function (sa, salen) { // family / port offsets are common to both sockaddr_in and sockaddr_in6 - var family = {{{ makeGetValue('sa', 'Sockets.sockaddr_in_layout.sin_family', 'i16') }}}; - var port = _ntohs({{{ makeGetValue('sa', 'Sockets.sockaddr_in_layout.sin_port', 'i16') }}}); + var family = {{{ makeGetValue('sa', C_STRUCTS.sockaddr_in.sin_family, 'i16') }}}; + var port = _ntohs({{{ makeGetValue('sa', C_STRUCTS.sockaddr_in.sin_port, 'i16') }}}); var addr; switch (family) { case {{{ cDefine('AF_INET') }}}: - if (salen !== Sockets.sockaddr_in_layout.__size__) { + if (salen !== {{{ C_STRUCTS.sockaddr_in.__size__ }}}) { return { errno: ERRNO_CODES.EINVAL }; } - addr = {{{ makeGetValue('sa', 'Sockets.sockaddr_in_layout.sin_addr', 'i32') }}}; + addr = {{{ makeGetValue('sa', C_STRUCTS.sockaddr_in.sin_addr.s_addr, 'i32') }}}; addr = __inet_ntop4_raw(addr); break; case {{{ cDefine('AF_INET6') }}}: - if (salen !== Sockets.sockaddr_in6_layout.__size__) { + if (salen !== {{{ C_STRUCTS.sockaddr_in6.__size__ }}}) { return { errno: ERRNO_CODES.EINVAL }; } addr = [ - {{{ makeGetValue('sa', 'Sockets.sockaddr_in6_layout.sin6_addr+0', 'i32') }}}, - {{{ makeGetValue('sa', 'Sockets.sockaddr_in6_layout.sin6_addr+4', 'i32') }}}, - {{{ makeGetValue('sa', 'Sockets.sockaddr_in6_layout.sin6_addr+8', 'i32') }}}, - {{{ makeGetValue('sa', 'Sockets.sockaddr_in6_layout.sin6_addr+12', 'i32') }}} + {{{ makeGetValue('sa', C_STRUCTS.sockaddr_in6.sin6_addr.__in6_union.__s6_addr+0, 'i32') }}}, + {{{ makeGetValue('sa', C_STRUCTS.sockaddr_in6.sin6_addr.__in6_union.__s6_addr+4, 'i32') }}}, + {{{ makeGetValue('sa', C_STRUCTS.sockaddr_in6.sin6_addr.__in6_union.__s6_addr+8, 'i32') }}}, + {{{ makeGetValue('sa', C_STRUCTS.sockaddr_in6.sin6_addr.__in6_union.__s6_addr+12, 'i32') }}} ]; addr = __inet_ntop6_raw(addr); break; @@ -8215,18 +8069,18 @@ LibraryManager.library = { switch (family) { case {{{ cDefine('AF_INET') }}}: addr = __inet_pton4_raw(addr); - {{{ makeSetValue('sa', 'Sockets.sockaddr_in_layout.sin_family', 'family', 'i16') }}}; - {{{ makeSetValue('sa', 'Sockets.sockaddr_in_layout.sin_addr', 'addr', 'i32') }}}; - {{{ makeSetValue('sa', 'Sockets.sockaddr_in_layout.sin_port', '_htons(port)', 'i16') }}}; + {{{ makeSetValue('sa', C_STRUCTS.sockaddr_in.sin_family, 'family', 'i16') }}}; + {{{ makeSetValue('sa', C_STRUCTS.sockaddr_in.sin_addr.s_addr, 'addr', 'i32') }}}; + {{{ makeSetValue('sa', C_STRUCTS.sockaddr_in.sin_port, '_htons(port)', 'i16') }}}; break; case {{{ cDefine('AF_INET6') }}}: addr = __inet_pton6_raw(addr); - {{{ makeSetValue('sa', 'Sockets.sockaddr_in6_layout.sin6_family', 'family', 'i32') }}}; - {{{ makeSetValue('sa', 'Sockets.sockaddr_in6_layout.sin6_addr+0', 'addr[0]', 'i32') }}}; - {{{ makeSetValue('sa', 'Sockets.sockaddr_in6_layout.sin6_addr+4', 'addr[1]', 'i32') }}}; - {{{ makeSetValue('sa', 'Sockets.sockaddr_in6_layout.sin6_addr+8', 'addr[2]', 'i32') }}}; - {{{ makeSetValue('sa', 'Sockets.sockaddr_in6_layout.sin6_addr+12', 'addr[3]', 'i32') }}}; - {{{ makeSetValue('sa', 'Sockets.sockaddr_in6_layout.sin6_port', '_htons(port)', 'i16') }}}; + {{{ makeSetValue('sa', C_STRUCTS.sockaddr_in6.sin6_family, 'family', 'i32') }}}; + {{{ makeSetValue('sa', C_STRUCTS.sockaddr_in6.sin6_addr.__in6_union.__s6_addr+0, 'addr[0]', 'i32') }}}; + {{{ makeSetValue('sa', C_STRUCTS.sockaddr_in6.sin6_addr.__in6_union.__s6_addr+4, 'addr[1]', 'i32') }}}; + {{{ makeSetValue('sa', C_STRUCTS.sockaddr_in6.sin6_addr.__in6_union.__s6_addr+8, 'addr[2]', 'i32') }}}; + {{{ makeSetValue('sa', C_STRUCTS.sockaddr_in6.sin6_addr.__in6_union.__s6_addr+12, 'addr[3]', 'i32') }}}; + {{{ makeSetValue('sa', C_STRUCTS.sockaddr_in6.sin6_port, '_htons(port)', 'i16') }}}; break; default: return { errno: ERRNO_CODES.EAFNOSUPPORT }; @@ -8472,14 +8326,14 @@ LibraryManager.library = { return -1; } - var iov = {{{ makeGetValue('message', 'Sockets.msghdr_layout.msg_iov', '*') }}}; - var num = {{{ makeGetValue('message', 'Sockets.msghdr_layout.msg_iovlen', 'i32') }}}; + var iov = {{{ makeGetValue('message', C_STRUCTS.msghdr.msg_iov, '*') }}}; + var num = {{{ makeGetValue('message', C_STRUCTS.msghdr.msg_iovlen, 'i32') }}}; // read the address and port to send to var addr; var port; - var name = {{{ makeGetValue('message', 'Sockets.msghdr_layout.msg_name', '*') }}}; - var namelen = {{{ makeGetValue('message', 'Sockets.msghdr_layout.msg_namelen', 'i32') }}}; + var name = {{{ makeGetValue('message', C_STRUCTS.msghdr.msg_name, '*') }}}; + var namelen = {{{ makeGetValue('message', C_STRUCTS.msghdr.msg_namelen, 'i32') }}}; if (name) { var info = __read_sockaddr(name, namelen); if (info.errno) { @@ -8493,13 +8347,13 @@ LibraryManager.library = { // concatenate scatter-gather arrays into one message buffer var total = 0; for (var i = 0; i < num; i++) { - total += {{{ makeGetValue('iov', '(Sockets.iovec_layout.__size__ * i) + Sockets.iovec_layout.iov_len', 'i32') }}}; + total += {{{ makeGetValue('iov', '(' + C_STRUCTS.iovec.__size__ + ' * i) + ' + C_STRUCTS.iovec.iov_len, 'i32') }}}; } var view = new Uint8Array(total); var offset = 0; for (var i = 0; i < num; i++) { - var iovbase = {{{ makeGetValue('iov', '(Sockets.iovec_layout.__size__ * i) + Sockets.iovec_layout.iov_base', 'i8*') }}}; - var iovlen = {{{ makeGetValue('iov', '(Sockets.iovec_layout.__size__ * i) + Sockets.iovec_layout.iov_len', 'i32') }}}; + var iovbase = {{{ makeGetValue('iov', '(' + C_STRUCTS.iovec.__size__ + ' * i) + ' + C_STRUCTS.iovec.iov_base, 'i8*') }}}; + var iovlen = {{{ makeGetValue('iov', '(' + C_STRUCTS.iovec.__size__ + ' * i) + ' + C_STRUCTS.iovec.iov_len, 'i32') }}}; for (var j = 0; j < iovlen; j++) { view[offset++] = {{{ makeGetValue('iovbase', 'j', 'i8') }}}; } @@ -8522,13 +8376,13 @@ LibraryManager.library = { return -1; } - var iov = {{{ makeGetValue('message', 'Sockets.msghdr_layout.msg_iov', 'i8*') }}}; - var num = {{{ makeGetValue('message', 'Sockets.msghdr_layout.msg_iovlen', 'i32') }}}; + var iov = {{{ makeGetValue('message', C_STRUCTS.msghdr.msg_iov, 'i8*') }}}; + var num = {{{ makeGetValue('message', C_STRUCTS.msghdr.msg_iovlen, 'i32') }}}; // get the total amount of data we can read across all arrays var total = 0; for (var i = 0; i < num; i++) { - total += {{{ makeGetValue('iov', '(Sockets.iovec_layout.__size__ * i) + Sockets.iovec_layout.iov_len', 'i32') }}}; + total += {{{ makeGetValue('iov', '(' + C_STRUCTS.iovec.__size__ + ' * i) + ' + C_STRUCTS.iovec.iov_len, 'i32') }}}; } // try to read total data @@ -8554,7 +8408,7 @@ LibraryManager.library = { // Requests that the function block until the full amount of data requested can be returned. The function may return a smaller amount of data if a signal is caught, if the connection is terminated, if MSG_PEEK was specified, or if an error is pending for the socket. // write the source address out - var name = {{{ makeGetValue('message', 'Sockets.msghdr_layout.msg_name', '*') }}}; + var name = {{{ makeGetValue('message', C_STRUCTS.msghdr.msg_name, '*') }}}; if (name) { var res = __write_sockaddr(name, sock.family, DNS.lookup_name(msg.addr), msg.port); assert(!res.errno); @@ -8564,8 +8418,8 @@ LibraryManager.library = { var bytesRemaining = msg.buffer.byteLength; for (var i = 0; bytesRemaining > 0 && i < num; i++) { - var iovbase = {{{ makeGetValue('iov', '(Sockets.iovec_layout.__size__ * i) + Sockets.iovec_layout.iov_base', 'i8*') }}}; - var iovlen = {{{ makeGetValue('iov', '(Sockets.iovec_layout.__size__ * i) + Sockets.iovec_layout.iov_len', 'i32') }}}; + var iovbase = {{{ makeGetValue('iov', '(' + C_STRUCTS.iovec.__size__ + ' * i) + ' + C_STRUCTS.iovec.iov_base, 'i8*') }}}; + var iovlen = {{{ makeGetValue('iov', '(' + C_STRUCTS.iovec.__size__ + ' * i) + ' + C_STRUCTS.iovec.iov_len, 'i32') }}}; if (!iovlen) { continue; } @@ -8759,6 +8613,15 @@ LibraryManager.library = { Runtime.stackAlloc(-4*i); // free up the stack space we know is ok to free }, + emscripten_asm_const: function(code) { + // code is a constant string on the heap, so we can cache these + if (!Runtime.asmConstCache) Runtime.asmConstCache = {}; + var func = Runtime.asmConstCache[code]; + if (func) return func(); + func = Runtime.asmConstCache[code] = eval('(function(){ ' + Pointer_stringify(code) + ' })'); // new Function does not allow upvars in node + return func(); + }, + //============================ // i64 math //============================ @@ -8854,7 +8717,7 @@ function autoAddDeps(object, name) { } // Add aborting stubs for various libc stuff needed by libc++ -['pthread_cond_signal', 'pthread_equal', 'wcstol', 'wcstoll', 'wcstoul', 'wcstoull', 'wcstof', 'wcstod', 'wcstold', 'swprintf', 'pthread_join', 'pthread_detach', 'strcoll_l', 'strxfrm_l', 'wcscoll_l', 'toupper_l', 'tolower_l', 'iswspace_l', 'iswprint_l', 'iswcntrl_l', 'iswupper_l', 'iswlower_l', 'iswalpha_l', 'iswdigit_l', 'iswpunct_l', 'iswxdigit_l', 'iswblank_l', 'wcsxfrm_l', 'towupper_l', 'towlower_l', 'catgets', 'catopen', 'catclose'].forEach(function(aborter) { +['pthread_cond_signal', 'pthread_equal', 'wcstol', 'wcstoll', 'wcstoul', 'wcstoull', 'wcstof', 'wcstod', 'wcstold', 'pthread_join', 'pthread_detach', 'catgets', 'catopen', 'catclose', 'fputwc', '__lockfile', '__unlockfile'].forEach(function(aborter) { LibraryManager.library[aborter] = function() { throw 'TODO: ' + aborter }; }); diff --git a/src/library_fs.js b/src/library_fs.js index 84a5245b..dc5c20f8 100644 --- a/src/library_fs.js +++ b/src/library_fs.js @@ -1,5 +1,5 @@ mergeInto(LibraryManager.library, { - $FS__deps: ['$ERRNO_CODES', '$ERRNO_MESSAGES', '__setErrNo', '$VFS', '$PATH', '$TTY', '$MEMFS', 'stdin', 'stdout', 'stderr', 'fflush'], + $FS__deps: ['$ERRNO_CODES', '$ERRNO_MESSAGES', '__setErrNo', '$VFS', '$PATH', '$TTY', '$MEMFS', '$IDBFS', '$NODEFS', 'stdin', 'stdout', 'stderr', 'fflush'], $FS__postset: 'FS.staticInit();' + '__ATINIT__.unshift({ func: function() { if (!Module["noFSInit"] && !FS.init.initialized) FS.init() } });' + '__ATMAIN__.push({ func: function() { FS.ignorePermissions = false } });' + @@ -14,6 +14,7 @@ mergeInto(LibraryManager.library, { 'Module["FS_createDevice"] = FS.createDevice;', $FS: { root: null, + mounts: [], devices: [null], streams: [null], nextInode: 1, @@ -50,11 +51,8 @@ mergeInto(LibraryManager.library, { // // paths // - cwd: function() { - return FS.currentPath; - }, lookupPath: function(path, opts) { - path = PATH.resolve(FS.currentPath, path); + path = PATH.resolve(FS.cwd(), path); opts = opts || { recurse_count: 0 }; if (opts.recurse_count > 8) { // max recursive lookup of 8 @@ -122,6 +120,11 @@ mergeInto(LibraryManager.library, { // hashName: function(parentid, name) { var hash = 0; + +#if CASE_INSENSITIVE_FS + name = name.toLowerCase(); +#endif + for (var i = 0; i < name.length; i++) { hash = ((hash << 5) - hash + name.charCodeAt(i)) | 0; } @@ -153,8 +156,15 @@ mergeInto(LibraryManager.library, { throw new FS.ErrnoError(err); } var hash = FS.hashName(parent.id, name); +#if CASE_INSENSITIVE_FS + name = name.toLowerCase(); +#endif for (var node = FS.nameTable[hash]; node; node = node.name_next) { - if (node.parent.id === parent.id && node.name === name) { + var nodeName = node.name; +#if CASE_INSENSITIVE_FS + nodeName = nodeName.toLowerCase(); +#endif + if (node.parent.id === parent.id && nodeName === name) { return node; } } @@ -309,7 +319,7 @@ mergeInto(LibraryManager.library, { if (!FS.isDir(node.mode)) { return ERRNO_CODES.ENOTDIR; } - if (FS.isRoot(node) || FS.getPath(node) === FS.currentPath) { + if (FS.isRoot(node) || FS.getPath(node) === FS.cwd()) { return ERRNO_CODES.EBUSY; } } else { @@ -422,17 +432,45 @@ mergeInto(LibraryManager.library, { // // core // + syncfs: function(populate, callback) { + if (typeof(populate) === 'function') { + callback = populate; + populate = false; + } + + var completed = 0; + var total = FS.mounts.length; + var done = function(err) { + if (err) { + return callback(err); + } + if (++completed >= total) { + callback(null); + } + }; + + // sync all mounts + for (var i = 0; i < FS.mounts.length; i++) { + var mount = FS.mounts[i]; + if (!mount.type.syncfs) { + done(null); + continue; + } + mount.type.syncfs(mount, populate, done); + } + }, mount: function(type, opts, mountpoint) { + var lookup; + if (mountpoint) { + lookup = FS.lookupPath(mountpoint, { follow: false }); + mountpoint = lookup.path; // use the absolute path + } var mount = { type: type, opts: opts, mountpoint: mountpoint, root: null }; - var lookup; - if (mountpoint) { - lookup = FS.lookupPath(mountpoint, { follow: false }); - } // create a root node for the fs var root = type.mount(mount); root.mount = mount; @@ -446,6 +484,8 @@ mergeInto(LibraryManager.library, { FS.root = mount.root; } } + // add to our cached list of mounts + FS.mounts.push(mount); return root; }, lookup: function(parent, name) { @@ -759,7 +799,6 @@ mergeInto(LibraryManager.library, { follow: !(flags & {{{ cDefine('O_NOFOLLOW') }}}) }); node = lookup.node; - path = lookup.path; } catch (e) { // ignore } @@ -791,10 +830,13 @@ mergeInto(LibraryManager.library, { if ((flags & {{{ cDefine('O_TRUNC')}}})) { FS.truncate(node, 0); } + // we've already handled these, don't pass down to the underlying vfs + flags &= ~({{{ cDefine('O_EXCL') }}} | {{{ cDefine('O_TRUNC') }}}); + // register the stream with the filesystem var stream = FS.createStream({ - path: path, node: node, + path: FS.getPath(node), // we want the absolute path to the node flags: flags, seekable: true, position: 0, @@ -959,8 +1001,21 @@ mergeInto(LibraryManager.library, { // // module-level FS code - // TODO move to pre/postamble // + cwd: function() { + return FS.currentPath; + }, + chdir: function(path) { + var lookup = FS.lookupPath(path, { follow: true }); + if (!FS.isDir(lookup.node.mode)) { + throw new FS.ErrnoError(ERRNO_CODES.ENOTDIR); + } + var err = FS.nodePermissions(lookup.node, 'x'); + if (err) { + throw new FS.ErrnoError(err); + } + FS.currentPath = lookup.path; + }, createDefaultDirectories: function() { FS.mkdir('/tmp'); }, diff --git a/src/library_gl.js b/src/library_gl.js index 16ea5531..83e68777 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -217,6 +217,23 @@ var LibraryGL = { throw 'Invalid format (' + format + ')'; } break; + case 0x1403 /* GL_UNSIGNED_SHORT */: + if (format == 0x1902 /* GL_DEPTH_COMPONENT */) { + sizePerPixel = 2; + } else { + throw 'Invalid format (' + format + ')'; + } + break; + case 0x1405 /* GL_UNSIGNED_INT */: + if (format == 0x1902 /* GL_DEPTH_COMPONENT */) { + sizePerPixel = 4; + } else { + throw 'Invalid format (' + format + ')'; + } + break; + case 0x84FA /* UNSIGNED_INT_24_8_WEBGL */: + sizePerPixel = 4; + break; case 0x8363 /* GL_UNSIGNED_SHORT_5_6_5 */: case 0x8033 /* GL_UNSIGNED_SHORT_4_4_4_4 */: case 0x8034 /* GL_UNSIGNED_SHORT_5_5_5_1 */: @@ -244,6 +261,8 @@ var LibraryGL = { pixels = {{{ makeHEAPView('U8', 'pixels', 'pixels+bytes') }}}; } else if (type == 0x1406 /* GL_FLOAT */) { pixels = {{{ makeHEAPView('F32', 'pixels', 'pixels+bytes') }}}; + } else if (type == 0x1405 /* GL_UNSIGNED_INT */ || type == 0x84FA /* UNSIGNED_INT_24_8_WEBGL */) { + pixels = {{{ makeHEAPView('U32', 'pixels', 'pixels+bytes') }}}; } else { pixels = {{{ makeHEAPView('U16', 'pixels', 'pixels+bytes') }}}; } @@ -291,6 +310,9 @@ var LibraryGL = { Module.ctx.bufferSubData(Module.ctx.ARRAY_BUFFER, 0, HEAPU8.subarray(cb.ptr, cb.ptr + size)); +#if GL_ASSERTIONS + GL.validateVertexAttribPointer(cb.size, cb.type, cb.stride, 0); +#endif Module.ctx.vertexAttribPointer(i, cb.size, cb.type, cb.normalized, cb.stride, 0); } }, @@ -302,6 +324,56 @@ var LibraryGL = { }, #endif +#if GL_ASSERTIONS + validateGLObjectID: function(objectHandleArray, objectID, callerFunctionName, objectReadableType) { + if (objectID != 0) { + if (objectHandleArray[objectID] === null) { + console.error(callerFunctionName + ' called with an already deleted ' + objectReadableType + ' ID ' + objectID + '!'); + } else if (!objectHandleArray[objectID]) { + console.error(callerFunctionName + ' called with an invalid ' + objectReadableType + ' ID ' + objectID + '!'); + } + } + }, + // Validates that user obeys GL spec #6.4: http://www.khronos.org/registry/webgl/specs/latest/1.0/#6.4 + validateVertexAttribPointer: function(dimension, dataType, stride, offset) { + var sizeBytes = 1; + switch(dataType) { + case 0x1400 /* GL_BYTE */: + case 0x1401 /* GL_UNSIGNED_BYTE */: + sizeBytes = 1; + break; + case 0x1402 /* GL_SHORT */: + case 0x1403 /* GL_UNSIGNED_SHORT */: + sizeBytes = 2; + break; + case 0x1404 /* GL_INT */: + case 0x1405 /* GL_UNSIGNED_INT */: + case 0x1406 /* GL_FLOAT */: + sizeBytes = 4; + break; + case 0x140A /* GL_DOUBLE */: + sizeBytes = 8; + break; + default: + console.error('Invalid vertex attribute data type GLenum ' + dataType + ' passed to GL function!'); + } + if (dimension == 0x80E1 /* GL_BGRA */) { + console.error('WebGL does not support size=GL_BGRA in a call to glVertexAttribPointer! Please use size=4 and type=GL_UNSIGNED_BYTE instead!'); + } else if (dimension < 1 || dimension > 4) { + console.error('Invalid dimension='+dimension+' in call to glVertexAttribPointer, must be 1,2,3 or 4.'); + } + if (stride < 0 || stride > 255) { + console.error('Invalid stride='+stride+' in call to glVertexAttribPointer. Note that maximum supported stride in WebGL is 255!'); + } + if (offset % sizeBytes != 0) { + console.error('GL spec section 6.4 error: vertex attribute data offset of ' + offset + ' bytes should have been a multiple of the data type size that was used: GLenum ' + dataType + ' has size of ' + sizeBytes + ' bytes!'); + } + if (stride % sizeBytes != 0) { + console.error('GL spec section 6.4 error: vertex attribute data stride of ' + stride + ' bytes should have been a multiple of the data type size that was used: GLenum ' + dataType + ' has size of ' + sizeBytes + ' bytes!'); + } + }, +#endif + initExtensions: function() { if (GL.initExtensions.done) return; GL.initExtensions.done = true; @@ -334,6 +406,10 @@ var LibraryGL = { GL.elementIndexUintExt = Module.ctx.getExtension('OES_element_index_uint'); GL.standardDerivativesExt = Module.ctx.getExtension('OES_standard_derivatives'); + + GL.depthTextureExt = Module.ctx.getExtension("WEBGL_depth_texture") || + Module.ctx.getExtension("MOZ_WEBGL_depth_texture") || + Module.ctx.getExtension("WEBKIT_WEBGL_depth_texture"); } }, @@ -588,6 +664,9 @@ var LibraryGL = { glBindTexture__sig: 'vii', glBindTexture: function(target, texture) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.textures, texture, 'glBindTexture', 'texture'); +#endif Module.ctx.bindTexture(target, texture ? GL.textures[texture] : null); }, @@ -710,6 +789,9 @@ var LibraryGL = { glBindRenderbuffer__sig: 'vii', glBindRenderbuffer: function(target, renderbuffer) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.renderbuffers, renderbuffer, 'glBindRenderbuffer', 'renderbuffer'); +#endif Module.ctx.bindRenderbuffer(target, renderbuffer ? GL.renderbuffers[renderbuffer] : null); }, @@ -727,6 +809,9 @@ var LibraryGL = { glGetUniformfv__sig: 'viii', glGetUniformfv: function(program, location, params) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.programs, program, 'glGetUniformfv', 'program'); +#endif var data = Module.ctx.getUniform(GL.programs[program], GL.uniforms[location]); if (typeof data == 'number') { {{{ makeSetValue('params', '0', 'data', 'float') }}}; @@ -739,6 +824,9 @@ var LibraryGL = { glGetUniformiv__sig: 'viii', glGetUniformiv: function(program, location, params) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.programs, program, 'glGetUniformiv', 'program'); +#endif var data = Module.ctx.getUniform(GL.programs[program], GL.uniforms[location]); if (typeof data == 'number' || typeof data == 'boolean') { {{{ makeSetValue('params', '0', 'data', 'i32') }}}; @@ -751,6 +839,9 @@ var LibraryGL = { glGetUniformLocation__sig: 'iii', glGetUniformLocation: function(program, name) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.programs, program, 'glGetUniformLocation', 'program'); +#endif name = Pointer_stringify(name); var ptable = GL.uniformTable[program]; if (!ptable) ptable = GL.uniformTable[program] = {}; @@ -810,6 +901,9 @@ var LibraryGL = { glGetActiveUniform__sig: 'viiiiiii', glGetActiveUniform: function(program, index, bufSize, length, size, type, name) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.programs, program, 'glGetActiveUniform', 'program'); +#endif program = GL.programs[program]; var info = Module.ctx.getActiveUniform(program, index); @@ -1018,6 +1112,9 @@ var LibraryGL = { glBindBuffer__sig: 'vii', glBindBuffer: function(target, buffer) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.buffers, buffer, 'glBindBuffer', 'buffer'); +#endif var bufferObj = buffer ? GL.buffers[buffer] : null; if (target == Module.ctx.ARRAY_BUFFER) { @@ -1062,6 +1159,9 @@ var LibraryGL = { glGetActiveAttrib__sig: 'viiiiiii', glGetActiveAttrib: function(program, index, bufSize, length, size, type, name) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.programs, program, 'glGetActiveAttrib', 'program'); +#endif program = GL.programs[program]; var info = Module.ctx.getActiveAttrib(program, index); @@ -1094,6 +1194,9 @@ var LibraryGL = { glGetAttachedShaders__sig: 'viiii', glGetAttachedShaders: function(program, maxCount, count, shaders) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.programs, program, 'glGetAttachedShaders', 'program'); +#endif var result = Module.ctx.getAttachedShaders(GL.programs[program]); var len = result.length; if (len > maxCount) { @@ -1109,12 +1212,18 @@ var LibraryGL = { glShaderSource__sig: 'viiii', glShaderSource: function(shader, count, string, length) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.shaders, shader, 'glShaderSource', 'shader'); +#endif var source = GL.getSource(shader, count, string, length); Module.ctx.shaderSource(GL.shaders[shader], source); }, glGetShaderSource__sig: 'viiii', glGetShaderSource: function(shader, bufSize, length, source) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.shaders, shader, 'glGetShaderSource', 'shader'); +#endif var result = Module.ctx.getShaderSource(GL.shaders[shader]); result = result.slice(0, Math.max(0, bufSize - 1)); writeStringToMemory(result, source); @@ -1125,11 +1234,17 @@ var LibraryGL = { glCompileShader__sig: 'vi', glCompileShader: function(shader) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.shaders, shader, 'glCompileShader', 'shader'); +#endif Module.ctx.compileShader(GL.shaders[shader]); }, glGetShaderInfoLog__sig: 'viiii', glGetShaderInfoLog: function(shader, maxLength, length, infoLog) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.shaders, shader, 'glGetShaderInfoLog', 'shader'); +#endif var log = Module.ctx.getShaderInfoLog(GL.shaders[shader]); // Work around a bug in Chromium which causes getShaderInfoLog to return null if (!log) { @@ -1144,6 +1259,9 @@ var LibraryGL = { glGetShaderiv__sig: 'viii', glGetShaderiv : function(shader, pname, p) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.shaders, shader, 'glGetShaderiv', 'shader'); +#endif if (pname == 0x8B84) { // GL_INFO_LOG_LENGTH {{{ makeSetValue('p', '0', 'Module.ctx.getShaderInfoLog(GL.shaders[shader]).length + 1', 'i32') }}}; } else { @@ -1153,6 +1271,9 @@ var LibraryGL = { glGetProgramiv__sig: 'viii', glGetProgramiv : function(program, pname, p) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.programs, program, 'glGetProgramiv', 'program'); +#endif if (pname == 0x8B84) { // GL_INFO_LOG_LENGTH {{{ makeSetValue('p', '0', 'Module.ctx.getProgramInfoLog(GL.programs[program]).length + 1', 'i32') }}}; } else { @@ -1187,12 +1308,20 @@ var LibraryGL = { glAttachShader__sig: 'vii', glAttachShader: function(program, shader) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.programs, program, 'glAttachShader', 'program'); + GL.validateGLObjectID(GL.shaders, shader, 'glAttachShader', 'shader'); +#endif Module.ctx.attachShader(GL.programs[program], GL.shaders[shader]); }, glDetachShader__sig: 'vii', glDetachShader: function(program, shader) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.programs, program, 'glDetachShader', 'program'); + GL.validateGLObjectID(GL.shaders, shader, 'glDetachShader', 'shader'); +#endif Module.ctx.detachShader(GL.programs[program], GL.shaders[shader]); }, @@ -1206,12 +1335,18 @@ var LibraryGL = { glLinkProgram__sig: 'vi', glLinkProgram: function(program) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.programs, program, 'glLinkProgram', 'program'); +#endif Module.ctx.linkProgram(GL.programs[program]); GL.uniformTable[program] = {}; // uniforms no longer keep the same names after linking }, glGetProgramInfoLog__sig: 'viiii', glGetProgramInfoLog: function(program, maxLength, length, infoLog) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.programs, program, 'glGetProgramInfoLog', 'program'); +#endif var log = Module.ctx.getProgramInfoLog(GL.programs[program]); // Work around a bug in Chromium which causes getProgramInfoLog to return null if (!log) { @@ -1226,11 +1361,17 @@ var LibraryGL = { glUseProgram__sig: 'vi', glUseProgram: function(program) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.programs, program, 'glUseProgram', 'program'); +#endif Module.ctx.useProgram(program ? GL.programs[program] : null); }, glValidateProgram__sig: 'vi', glValidateProgram: function(program) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.programs, program, 'glValidateProgram', 'program'); +#endif Module.ctx.validateProgram(GL.programs[program]); }, @@ -1243,12 +1384,18 @@ var LibraryGL = { glBindAttribLocation__sig: 'viii', glBindAttribLocation: function(program, index, name) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.programs, program, 'glBindAttribLocation', 'program'); +#endif name = Pointer_stringify(name); Module.ctx.bindAttribLocation(GL.programs[program], index, name); }, glBindFramebuffer__sig: 'vii', glBindFramebuffer: function(target, framebuffer) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.framebuffers, framebuffer, 'glBindFramebuffer', 'framebuffer'); +#endif Module.ctx.bindFramebuffer(target, framebuffer ? GL.framebuffers[framebuffer] : null); }, @@ -1276,12 +1423,18 @@ var LibraryGL = { glFramebufferRenderbuffer__sig: 'viiii', glFramebufferRenderbuffer: function(target, attachment, renderbuffertarget, renderbuffer) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.renderbuffers, renderbuffer, 'glFramebufferRenderbuffer', 'renderbuffer'); +#endif Module.ctx.framebufferRenderbuffer(target, attachment, renderbuffertarget, GL.renderbuffers[renderbuffer]); }, glFramebufferTexture2D__sig: 'viiiii', glFramebufferTexture2D: function(target, attachment, textarget, texture, level) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.textures, texture, 'glFramebufferTexture2D', 'texture'); +#endif Module.ctx.framebufferTexture2D(target, attachment, textarget, GL.textures[texture], level); }, @@ -3161,6 +3314,9 @@ var LibraryGL = { var clientAttributes = GL.immediate.clientAttributes; +#if GL_ASSERTIONS + GL.validateVertexAttribPointer(positionSize, positionType, GL.immediate.stride, clientAttributes[GL.immediate.VERTEX].offset); +#endif Module.ctx.vertexAttribPointer(this.positionLocation, positionSize, positionType, false, GL.immediate.stride, clientAttributes[GL.immediate.VERTEX].offset); Module.ctx.enableVertexAttribArray(this.positionLocation); @@ -3173,6 +3329,9 @@ var LibraryGL = { if (attribLoc === undefined || attribLoc < 0) continue; if (texUnitID < textureSizes.length && textureSizes[texUnitID]) { +#if GL_ASSERTIONS + GL.validateVertexAttribPointer(textureSizes[texUnitID], textureTypes[texUnitID], GL.immediate.stride, GL.immediate.clientAttributes[GL.immediate.TEXTURE0 + texUnitID].offset); +#endif Module.ctx.vertexAttribPointer(attribLoc, textureSizes[texUnitID], textureTypes[texUnitID], false, GL.immediate.stride, GL.immediate.clientAttributes[GL.immediate.TEXTURE0 + texUnitID].offset); Module.ctx.enableVertexAttribArray(attribLoc); @@ -3189,6 +3348,9 @@ var LibraryGL = { } } if (colorSize) { +#if GL_ASSERTIONS + GL.validateVertexAttribPointer(colorSize, colorType, GL.immediate.stride, clientAttributes[GL.immediate.COLOR].offset); +#endif Module.ctx.vertexAttribPointer(this.colorLocation, colorSize, colorType, true, GL.immediate.stride, clientAttributes[GL.immediate.COLOR].offset); Module.ctx.enableVertexAttribArray(this.colorLocation); @@ -3197,6 +3359,9 @@ var LibraryGL = { Module.ctx.vertexAttrib4fv(this.colorLocation, GL.immediate.clientColor); } if (this.hasNormal) { +#if GL_ASSERTIONS + GL.validateVertexAttribPointer(normalSize, normalType, GL.immediate.stride, clientAttributes[GL.immediate.NORMAL].offset); +#endif Module.ctx.vertexAttribPointer(this.normalLocation, normalSize, normalType, true, GL.immediate.stride, clientAttributes[GL.immediate.NORMAL].offset); Module.ctx.enableVertexAttribArray(this.normalLocation); @@ -4176,6 +4341,9 @@ var LibraryGL = { } cb.clientside = false; #endif +#if GL_ASSERTIONS + GL.validateVertexAttribPointer(size, type, stride, ptr); +#endif Module.ctx.vertexAttribPointer(index, size, type, normalized, stride, ptr); }, diff --git a/src/library_idbfs.js b/src/library_idbfs.js new file mode 100644 index 00000000..9031bad8 --- /dev/null +++ b/src/library_idbfs.js @@ -0,0 +1,216 @@ +mergeInto(LibraryManager.library, { + $IDBFS__deps: ['$FS', '$MEMFS', '$PATH'], + $IDBFS: { + dbs: {}, + indexedDB: function() { + return window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; + }, + DB_VERSION: 20, + DB_STORE_NAME: 'FILE_DATA', + // reuse all of the core MEMFS functionality + mount: function(mount) { + return MEMFS.mount.apply(null, arguments); + }, + // the only custom function IDBFS implements is to handle + // synchronizing the wrapped MEMFS with a backing IDB instance + syncfs: function(mount, populate, callback) { + IDBFS.getLocalSet(mount, function(err, local) { + if (err) return callback(err); + + IDBFS.getRemoteSet(mount, function(err, remote) { + if (err) return callback(err); + + var src = populate ? remote : local; + var dst = populate ? local : remote; + + IDBFS.reconcile(src, dst, callback); + }); + }); + }, + reconcile: function(src, dst, callback) { + var total = 0; + + var create = {}; + for (var key in src.files) { + if (!src.files.hasOwnProperty(key)) continue; + var e = src.files[key]; + var e2 = dst.files[key]; + if (!e2 || e.timestamp > e2.timestamp) { + create[key] = e; + total++; + } + } + + var remove = {}; + for (var key in dst.files) { + if (!dst.files.hasOwnProperty(key)) continue; + var e = dst.files[key]; + var e2 = src.files[key]; + if (!e2) { + remove[key] = e; + total++; + } + } + + if (!total) { + // early out + return callback(null); + } + + var completed = 0; + var done = function(err) { + if (err) return callback(err); + if (++completed >= total) { + return callback(null); + } + }; + + // create a single transaction to handle and IDB reads / writes we'll need to do + var db = src.type === 'remote' ? src.db : dst.db; + var transaction = db.transaction([IDBFS.DB_STORE_NAME], 'readwrite'); + transaction.onerror = function() { callback(this.error); }; + var store = transaction.objectStore(IDBFS.DB_STORE_NAME); + + for (var path in create) { + if (!create.hasOwnProperty(path)) continue; + var entry = create[path]; + + if (dst.type === 'local') { + // save file to local + try { + if (FS.isDir(entry.mode)) { + FS.mkdir(path, entry.mode); + } else if (FS.isFile(entry.mode)) { + var stream = FS.open(path, 'w+', 0666); + FS.write(stream, entry.contents, 0, entry.contents.length, 0, true /* canOwn */); + FS.close(stream); + } + done(null); + } catch (e) { + return done(e); + } + } else { + // save file to IDB + var req = store.put(entry, path); + req.onsuccess = function() { done(null); }; + req.onerror = function() { done(this.error); }; + } + } + + for (var path in remove) { + if (!remove.hasOwnProperty(path)) continue; + var entry = remove[path]; + + if (dst.type === 'local') { + // delete file from local + try { + if (FS.isDir(entry.mode)) { + // TODO recursive delete? + FS.rmdir(path); + } else if (FS.isFile(entry.mode)) { + FS.unlink(path); + } + done(null); + } catch (e) { + return done(e); + } + } else { + // delete file from IDB + var req = store.delete(path); + req.onsuccess = function() { done(null); }; + req.onerror = function() { done(this.error); }; + } + } + }, + getLocalSet: function(mount, callback) { + var files = {}; + + var isRealDir = function(p) { + return p !== '.' && p !== '..'; + }; + var toAbsolute = function(root) { + return function(p) { + return PATH.join(root, p); + } + }; + + var check = FS.readdir(mount.mountpoint) + .filter(isRealDir) + .map(toAbsolute(mount.mountpoint)); + + while (check.length) { + var path = check.pop(); + var stat, node; + + try { + var lookup = FS.lookupPath(path); + node = lookup.node; + stat = FS.stat(path); + } catch (e) { + return callback(e); + } + + if (FS.isDir(stat.mode)) { + check.push.apply(check, FS.readdir(path) + .filter(isRealDir) + .map(toAbsolute(path))); + + files[path] = { mode: stat.mode, timestamp: stat.mtime }; + } else if (FS.isFile(stat.mode)) { + files[path] = { contents: node.contents, mode: stat.mode, timestamp: stat.mtime }; + } else { + return callback(new Error('node type not supported')); + } + } + + return callback(null, { type: 'local', files: files }); + }, + getDB: function(name, callback) { + // look it up in the cache + var db = IDBFS.dbs[name]; + if (db) { + return callback(null, db); + } + var req; + try { + req = IDBFS.indexedDB().open(name, IDBFS.DB_VERSION); + } catch (e) { + return onerror(e); + } + req.onupgradeneeded = function() { + db = req.result; + db.createObjectStore(IDBFS.DB_STORE_NAME); + }; + req.onsuccess = function() { + db = req.result; + // add to the cache + IDBFS.dbs[name] = db; + callback(null, db); + }; + req.onerror = function() { + callback(this.error); + }; + }, + getRemoteSet: function(mount, callback) { + var files = {}; + + IDBFS.getDB(mount.mountpoint, function(err, db) { + if (err) return callback(err); + + var transaction = db.transaction([IDBFS.DB_STORE_NAME], 'readonly'); + transaction.onerror = function() { callback(this.error); }; + + var store = transaction.objectStore(IDBFS.DB_STORE_NAME); + store.openCursor().onsuccess = function(event) { + var cursor = event.target.result; + if (!cursor) { + return callback(null, { type: 'remote', db: db, files: files }); + } + + files[cursor.key] = cursor.value; + cursor.continue(); + }; + }); + } + } +}); diff --git a/src/library_memfs.js b/src/library_memfs.js index 28178d9f..4e56d996 100644 --- a/src/library_memfs.js +++ b/src/library_memfs.js @@ -5,18 +5,10 @@ mergeInto(LibraryManager.library, { CONTENT_OWNING: 1, // contains a subarray into the heap, and we own it, without copying (note: someone else needs to free() it, if that is necessary) CONTENT_FLEXIBLE: 2, // has been modified or never set to anything, and is a flexible js array that can grow/shrink CONTENT_FIXED: 3, // contains some fixed-size content written into it, in a typed array - ensureFlexible: function(node) { - if (node.contentMode !== MEMFS.CONTENT_FLEXIBLE) { - var contents = node.contents; - node.contents = Array.prototype.slice.call(contents); - node.contentMode = MEMFS.CONTENT_FLEXIBLE; - } - }, - mount: function(mount) { - return MEMFS.create_node(null, '/', {{{ cDefine('S_IFDIR') }}} | 0777, 0); + return MEMFS.createNode(null, '/', {{{ cDefine('S_IFDIR') }}} | 0777, 0); }, - create_node: function(parent, name, mode, dev) { + createNode: function(parent, name, mode, dev) { if (FS.isBlkdev(mode) || FS.isFIFO(mode)) { // no supported throw new FS.ErrnoError(ERRNO_CODES.EPERM); @@ -74,6 +66,13 @@ mergeInto(LibraryManager.library, { } return node; }, + ensureFlexible: function(node) { + if (node.contentMode !== MEMFS.CONTENT_FLEXIBLE) { + var contents = node.contents; + node.contents = Array.prototype.slice.call(contents); + node.contentMode = MEMFS.CONTENT_FLEXIBLE; + } + }, node_ops: { getattr: function(node) { var attr = {}; @@ -121,7 +120,7 @@ mergeInto(LibraryManager.library, { throw new FS.ErrnoError(ERRNO_CODES.ENOENT); }, mknod: function(parent, name, mode, dev) { - return MEMFS.create_node(parent, name, mode, dev); + return MEMFS.createNode(parent, name, mode, dev); }, rename: function(old_node, new_dir, new_name) { // if we're overwriting a directory at new_name, make sure it's empty. @@ -163,7 +162,7 @@ mergeInto(LibraryManager.library, { return entries; }, symlink: function(parent, newname, oldpath) { - var node = MEMFS.create_node(parent, newname, 0777 | {{{ cDefine('S_IFLNK') }}}, 0); + var node = MEMFS.createNode(parent, newname, 0777 | {{{ cDefine('S_IFLNK') }}}, 0); node.link = oldpath; return node; }, diff --git a/src/library_nodefs.js b/src/library_nodefs.js new file mode 100644 index 00000000..d8df1689 --- /dev/null +++ b/src/library_nodefs.js @@ -0,0 +1,234 @@ +mergeInto(LibraryManager.library, { + $NODEFS__deps: ['$FS', '$PATH'], + $NODEFS__postset: 'if (ENVIRONMENT_IS_NODE) { var fs = require("fs"); }', + $NODEFS: { + mount: function (mount) { + assert(ENVIRONMENT_IS_NODE); + return NODEFS.createNode(null, '/', NODEFS.getMode(mount.opts.root), 0); + }, + createNode: function (parent, name, mode, dev) { + if (!FS.isDir(mode) && !FS.isFile(mode) && !FS.isLink(mode)) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + var node = FS.createNode(parent, name, mode); + node.node_ops = NODEFS.node_ops; + node.stream_ops = NODEFS.stream_ops; + return node; + }, + getMode: function (path) { + var stat; + try { + stat = fs.lstatSync(path); + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + return stat.mode; + }, + realPath: function (node) { + var parts = []; + while (node.parent !== node) { + parts.push(node.name); + node = node.parent; + } + parts.push(node.mount.opts.root); + parts.reverse(); + return PATH.join.apply(null, parts); + }, + node_ops: { + getattr: function(node) { + var path = NODEFS.realPath(node); + var stat; + try { + stat = fs.lstatSync(path); + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + return { + dev: stat.dev, + ino: stat.ino, + mode: stat.mode, + nlink: stat.nlink, + uid: stat.uid, + gid: stat.gid, + rdev: stat.rdev, + size: stat.size, + atime: stat.atime, + mtime: stat.mtime, + ctime: stat.ctime, + blksize: stat.blksize, + blocks: stat.blocks + }; + }, + setattr: function(node, attr) { + var path = NODEFS.realPath(node); + try { + if (attr.mode !== undefined) { + fs.chmodSync(path, attr.mode); + // update the common node structure mode as well + node.mode = attr.mode; + } + if (attr.timestamp !== undefined) { + var date = new Date(attr.timestamp); + fs.utimesSync(path, date, date); + } + if (attr.size !== undefined) { + fs.truncateSync(path, attr.size); + } + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + }, + lookup: function (parent, name) { + var path = PATH.join(NODEFS.realPath(parent), name); + var mode = NODEFS.getMode(path); + return NODEFS.createNode(parent, name, mode); + }, + mknod: function (parent, name, mode, dev) { + var node = NODEFS.createNode(parent, name, mode, dev); + // create the backing node for this in the fs root as well + var path = NODEFS.realPath(node); + try { + if (FS.isDir(node.mode)) { + fs.mkdirSync(path, node.mode); + } else { + fs.writeFileSync(path, '', { mode: node.mode }); + } + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + return node; + }, + rename: function (oldNode, newDir, newName) { + var oldPath = NODEFS.realPath(oldNode); + var newPath = PATH.join(NODEFS.realPath(newDir), newName); + try { + fs.renameSync(oldPath, newPath); + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + }, + unlink: function(parent, name) { + var path = PATH.join(NODEFS.realPath(parent), name); + try { + fs.unlinkSync(path); + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + }, + rmdir: function(parent, name) { + var path = PATH.join(NODEFS.realPath(parent), name); + try { + fs.rmdirSync(path); + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + }, + readdir: function(node) { + var path = NODEFS.realPath(node); + try { + return fs.readdirSync(path); + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + }, + symlink: function(parent, newName, oldPath) { + var newPath = PATH.join(NODEFS.realPath(parent), newName); + try { + fs.symlinkSync(oldPath, newPath); + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + }, + readlink: function(node) { + var path = NODEFS.realPath(node); + try { + return fs.readlinkSync(path); + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + }, + }, + stream_ops: { + open: function (stream) { + var path = NODEFS.realPath(stream.node); + try { + if (FS.isFile(stream.node.mode)) { + stream.nfd = fs.openSync(path, stream.flags); + } + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + }, + close: function (stream) { + try { + if (FS.isFile(stream.node.mode)) { + fs.closeSync(stream.nfd); + } + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + }, + read: function (stream, buffer, offset, length, position) { + // FIXME this is terrible. + var nbuffer = new Buffer(length); + var res; + try { + res = fs.readSync(stream.nfd, nbuffer, 0, length, position); + } catch (e) { + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + if (res > 0) { + for (var i = 0; i < res; i++) { + buffer[offset + i] = nbuffer[i]; + } + } + return res; + }, + write: function (stream, buffer, offset, length, position) { + // FIXME this is terrible. + var nbuffer = new Buffer(buffer.subarray(offset, offset + length)); + var res; + try { + res = fs.writeSync(stream.nfd, nbuffer, 0, length, position); + } catch (e) { + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + return res; + }, + llseek: function (stream, offset, whence) { + var position = offset; + if (whence === 1) { // SEEK_CUR. + position += stream.position; + } else if (whence === 2) { // SEEK_END. + if (FS.isFile(stream.node.mode)) { + try { + var stat = fs.fstatSync(stream.nfd); + position += stat.size; + } catch (e) { + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + } + } + + if (position < 0) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + + stream.position = position; + return position; + } + } + } +});
\ No newline at end of file diff --git a/src/library_sdl.js b/src/library_sdl.js index 116bf547..27f2c0da 100644 --- a/src/library_sdl.js +++ b/src/library_sdl.js @@ -35,7 +35,7 @@ var LibrarySDL = { volume: 1.0 }, mixerFrequency: 22050, - mixerFormat: 0x8010, // AUDIO_S16LSB + mixerFormat: {{{ cDefine('AUDIO_S16LSB') }}}, //0x8010, // AUDIO_S16LSB mixerNumChannels: 2, mixerChunkSize: 1024, channelMinimumNumber: 0, @@ -151,89 +151,12 @@ var LibrarySDL = { 305: 224, // ctrl 308: 226, // alt }, - - structs: { - Rect: Runtime.generateStructInfo([ - ['i32', 'x'], ['i32', 'y'], ['i32', 'w'], ['i32', 'h'], - ]), - PixelFormat: Runtime.generateStructInfo([ - ['i32', 'format'], - ['void*', 'palette'], ['i8', 'BitsPerPixel'], ['i8', 'BytesPerPixel'], - ['i8', 'padding1'], ['i8', 'padding2'], - ['i32', 'Rmask'], ['i32', 'Gmask'], ['i32', 'Bmask'], ['i32', 'Amask'], - ['i8', 'Rloss'], ['i8', 'Gloss'], ['i8', 'Bloss'], ['i8', 'Aloss'], - ['i8', 'Rshift'], ['i8', 'Gshift'], ['i8', 'Bshift'], ['i8', 'Ashift'] - ]), - KeyboardEvent: Runtime.generateStructInfo([ - ['i32', 'type'], - ['i32', 'windowID'], - ['i8', 'state'], - ['i8', 'repeat'], - ['i8', 'padding2'], - ['i8', 'padding3'], - ['i32', 'keysym'] - ]), - keysym: Runtime.generateStructInfo([ - ['i32', 'scancode'], - ['i32', 'sym'], - ['i16', 'mod'], - ['i32', 'unicode'] - ]), - TextInputEvent: Runtime.generateStructInfo([ - ['i32', 'type'], - ['i32', 'windowID'], - ['b256', 'text'], - ]), - MouseMotionEvent: Runtime.generateStructInfo([ - ['i32', 'type'], - ['i32', 'windowID'], - ['i8', 'state'], - ['i8', 'padding1'], - ['i8', 'padding2'], - ['i8', 'padding3'], - ['i32', 'x'], - ['i32', 'y'], - ['i32', 'xrel'], - ['i32', 'yrel'] - ]), - MouseButtonEvent: Runtime.generateStructInfo([ - ['i32', 'type'], - ['i32', 'windowID'], - ['i8', 'button'], - ['i8', 'state'], - ['i8', 'padding1'], - ['i8', 'padding2'], - ['i32', 'x'], - ['i32', 'y'] - ]), - ResizeEvent: Runtime.generateStructInfo([ - ['i32', 'type'], - ['i32', 'w'], - ['i32', 'h'] - ]), - AudioSpec: Runtime.generateStructInfo([ - ['i32', 'freq'], - ['i16', 'format'], - ['i8', 'channels'], - ['i8', 'silence'], - ['i16', 'samples'], - ['i32', 'size'], - ['void*', 'callback'], - ['void*', 'userdata'] - ]), - version: Runtime.generateStructInfo([ - ['i8', 'major'], - ['i8', 'minor'], - ['i8', 'patch'] - ]) - }, - loadRect: function(rect) { return { - x: {{{ makeGetValue('rect + SDL.structs.Rect.x', '0', 'i32') }}}, - y: {{{ makeGetValue('rect + SDL.structs.Rect.y', '0', 'i32') }}}, - w: {{{ makeGetValue('rect + SDL.structs.Rect.w', '0', 'i32') }}}, - h: {{{ makeGetValue('rect + SDL.structs.Rect.h', '0', 'i32') }}} + x: {{{ makeGetValue('rect + ' + C_STRUCTS.SDL_Rect.x, '0', 'i32') }}}, + y: {{{ makeGetValue('rect + ' + C_STRUCTS.SDL_Rect.y, '0', 'i32') }}}, + w: {{{ makeGetValue('rect + ' + C_STRUCTS.SDL_Rect.w, '0', 'i32') }}}, + h: {{{ makeGetValue('rect + ' + C_STRUCTS.SDL_Rect.h, '0', 'i32') }}} }; }, @@ -261,35 +184,35 @@ var LibrarySDL = { makeSurface: function(width, height, flags, usePageCanvas, source, rmask, gmask, bmask, amask) { flags = flags || 0; - var surf = _malloc(15*Runtime.QUANTUM_SIZE); // SDL_Surface has 15 fields of quantum size + var surf = _malloc({{{ C_STRUCTS.SDL_Surface.__size__ }}}); // SDL_Surface has 15 fields of quantum size var buffer = _malloc(width*height*4); // TODO: only allocate when locked the first time - var pixelFormat = _malloc(18*Runtime.QUANTUM_SIZE); + var pixelFormat = _malloc({{{ C_STRUCTS.SDL_PixelFormat.__size__ }}}); flags |= 1; // SDL_HWSURFACE - this tells SDL_MUSTLOCK that this needs to be locked //surface with SDL_HWPALETTE flag is 8bpp surface (1 byte) var is_SDL_HWPALETTE = flags & 0x00200000; var bpp = is_SDL_HWPALETTE ? 1 : 4; - {{{ makeSetValue('surf+Runtime.QUANTUM_SIZE*0', '0', 'flags', 'i32') }}} // SDL_Surface.flags - {{{ makeSetValue('surf+Runtime.QUANTUM_SIZE*1', '0', 'pixelFormat', 'void*') }}} // SDL_Surface.format TODO - {{{ makeSetValue('surf+Runtime.QUANTUM_SIZE*2', '0', 'width', 'i32') }}} // SDL_Surface.w - {{{ makeSetValue('surf+Runtime.QUANTUM_SIZE*3', '0', 'height', 'i32') }}} // SDL_Surface.h - {{{ makeSetValue('surf+Runtime.QUANTUM_SIZE*4', '0', 'width * bpp', 'i32') }}} // SDL_Surface.pitch, assuming RGBA or indexed for now, + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.flags, 'flags', 'i32') }}} // SDL_Surface.flags + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.format, 'pixelFormat', 'void*') }}} // SDL_Surface.format TODO + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.w, 'width', 'i32') }}} // SDL_Surface.w + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.h, 'height', 'i32') }}} // SDL_Surface.h + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.pitch, 'width * bpp', 'i32') }}} // SDL_Surface.pitch, assuming RGBA or indexed for now, // since that is what ImageData gives us in browsers - {{{ makeSetValue('surf+Runtime.QUANTUM_SIZE*5', '0', 'buffer', 'void*') }}} // SDL_Surface.pixels - {{{ makeSetValue('surf+Runtime.QUANTUM_SIZE*6', '0', '0', 'i32*') }}} // SDL_Surface.offset + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.pixels, 'buffer', 'void*') }}} // SDL_Surface.pixels + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.clip_rect, '0', 'i32*') }}} // SDL_Surface.offset - {{{ makeSetValue('surf+Runtime.QUANTUM_SIZE*14', '0', '1', 'i32') }}} + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.refcount, '1', 'i32') }}} - {{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.format', '0', '-2042224636', 'i32') }}} // SDL_PIXELFORMAT_RGBA8888 - {{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.palette', '0', '0', 'i32') }}} // TODO - {{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.BitsPerPixel', '0', 'bpp * 8', 'i8') }}} - {{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.BytesPerPixel', '0', 'bpp', 'i8') }}} + {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.format, cDefine('SDL_PIXELFORMAT_RGBA8888'), 'i32') }}} // SDL_PIXELFORMAT_RGBA8888 + {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.palette, '0', 'i32') }}} // TODO + {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.BitsPerPixel, 'bpp * 8', 'i8') }}} + {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.BytesPerPixel, 'bpp', 'i8') }}} - {{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.Rmask', '0', 'rmask || 0x000000ff', 'i32') }}} - {{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.Gmask', '0', 'gmask || 0x0000ff00', 'i32') }}} - {{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.Bmask', '0', 'bmask || 0x00ff0000', 'i32') }}} - {{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.Amask', '0', 'amask || 0xff000000', 'i32') }}} + {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.Rmask, 'rmask || 0x000000ff', 'i32') }}} + {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.Gmask, 'gmask || 0x0000ff00', 'i32') }}} + {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.Bmask, 'bmask || 0x00ff0000', 'i32') }}} + {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.Amask, 'amask || 0xff000000', 'i32') }}} // Decide if we want to use WebGL or not var useWebGL = (flags & 0x04000000) != 0; // SDL_OPENGL @@ -367,7 +290,7 @@ var LibrarySDL = { }, freeSurface: function(surf) { - var refcountPointer = surf + Runtime.QUANTUM_SIZE * 14; + var refcountPointer = surf + {{{ C_STRUCTS.SDL_Surface.refcount }}}; var refcount = {{{ makeGetValue('refcountPointer', '0', 'i32') }}}; if (refcount > 1) { {{{ makeSetValue('refcountPointer', '0', 'refcount - 1', 'i32') }}}; @@ -608,7 +531,7 @@ var LibrarySDL = { makeCEvent: function(event, ptr) { if (typeof event === 'number') { // This is a pointer to a native C event that was SDL_PushEvent'ed - _memcpy(ptr, event, SDL.structs.KeyboardEvent.__size__); // XXX + _memcpy(ptr, event, {{{ C_STRUCTS.SDL_KeyboardEvent.__size__ }}}); // XXX return; } @@ -631,52 +554,52 @@ var LibrarySDL = { scan = SDL.scanCodes[key] || key; } - {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.type', 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}} - {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.state', 'down ? 1 : 0', 'i8') }}} - {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.repeat', '0', 'i8') }}} // TODO - {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.keysym + SDL.structs.keysym.scancode', 'scan', 'i32') }}} - {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.keysym + SDL.structs.keysym.sym', 'key', 'i32') }}} - {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.keysym + SDL.structs.keysym.mod', 'SDL.modState', 'i16') }}} + {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}} + {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.state, 'down ? 1 : 0', 'i8') }}} + {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.repeat, '0', 'i8') }}} // TODO + {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.keysym + C_STRUCTS.SDL_Keysym.scancode, 'scan', 'i32') }}} + {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.keysym + C_STRUCTS.SDL_Keysym.sym, 'key', 'i32') }}} + {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.keysym + C_STRUCTS.SDL_Keysym.mod, 'SDL.modState', 'i16') }}} // some non-character keys (e.g. backspace and tab) won't have keypressCharCode set, fill in with the keyCode. - {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.keysym + SDL.structs.keysym.unicode', 'event.keypressCharCode || key', 'i32') }}} + {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.keysym + C_STRUCTS.SDL_Keysym.unicode, 'event.keypressCharCode || key', 'i32') }}} break; } case 'keypress': { - {{{ makeSetValue('ptr', 'SDL.structs.TextInputEvent.type', 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}} + {{{ makeSetValue('ptr', C_STRUCTS.SDL_TextInputEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}} // Not filling in windowID for now var cStr = intArrayFromString(String.fromCharCode(event.charCode)); for (var i = 0; i < cStr.length; ++i) { - {{{ makeSetValue('ptr', 'SDL.structs.TextInputEvent.text + i', 'cStr[i]', 'i8') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_TextInputEvent.text + ' + i', 'cStr[i]', 'i8') }}}; } break; } case 'mousedown': case 'mouseup': case 'mousemove': { if (event.type != 'mousemove') { var down = event.type === 'mousedown'; - {{{ makeSetValue('ptr', 'SDL.structs.MouseButtonEvent.type', 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}}; - {{{ makeSetValue('ptr', 'SDL.structs.MouseButtonEvent.button', 'event.button+1', 'i8') }}}; // DOM buttons are 0-2, SDL 1-3 - {{{ makeSetValue('ptr', 'SDL.structs.MouseButtonEvent.state', 'down ? 1 : 0', 'i8') }}}; - {{{ makeSetValue('ptr', 'SDL.structs.MouseButtonEvent.x', 'Browser.mouseX', 'i32') }}}; - {{{ makeSetValue('ptr', 'SDL.structs.MouseButtonEvent.y', 'Browser.mouseY', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseButtonEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseButtonEvent.button, 'event.button+1', 'i8') }}}; // DOM buttons are 0-2, SDL 1-3 + {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseButtonEvent.state, 'down ? 1 : 0', 'i8') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseButtonEvent.x, 'Browser.mouseX', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseButtonEvent.y, 'Browser.mouseY', 'i32') }}}; } else { - {{{ makeSetValue('ptr', 'SDL.structs.MouseMotionEvent.type', 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}}; - {{{ makeSetValue('ptr', 'SDL.structs.MouseMotionEvent.state', 'SDL.buttonState', 'i8') }}}; - {{{ makeSetValue('ptr', 'SDL.structs.MouseMotionEvent.x', 'Browser.mouseX', 'i32') }}}; - {{{ makeSetValue('ptr', 'SDL.structs.MouseMotionEvent.y', 'Browser.mouseY', 'i32') }}}; - {{{ makeSetValue('ptr', 'SDL.structs.MouseMotionEvent.xrel', 'Browser.mouseMovementX', 'i32') }}}; - {{{ makeSetValue('ptr', 'SDL.structs.MouseMotionEvent.yrel', 'Browser.mouseMovementY', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseMotionEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseMotionEvent.state, 'SDL.buttonState', 'i8') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseMotionEvent.x, 'Browser.mouseX', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseMotionEvent.y, 'Browser.mouseY', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseMotionEvent.xrel, 'Browser.mouseMovementX', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseMotionEvent.yrel, 'Browser.mouseMovementY', 'i32') }}}; } break; } case 'unload': { - {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.type', 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}}; break; } case 'resize': { - {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.type', 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}}; - {{{ makeSetValue('ptr', 'SDL.structs.ResizeEvent.w', 'event.w', 'i32') }}}; - {{{ makeSetValue('ptr', 'SDL.structs.ResizeEvent.h', 'event.h', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_ResizeEvent.w, 'event.w', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_ResizeEvent.h, 'event.h', 'i32') }}}; break; } default: throw 'Unhandled SDL event: ' + event.type; @@ -704,7 +627,7 @@ var LibrarySDL = { // since the browser engine handles that for us. Therefore, in JS we just // maintain a list of channels and return IDs for them to the SDL consumer. allocateChannels: function(num) { // called from Mix_AllocateChannels and init - if (SDL.numChannels && SDL.numChannels >= num) return; + if (SDL.numChannels && SDL.numChannels >= num && num != 0) return; SDL.numChannels = num; SDL.channels = []; for (var i = 0; i < num; i++) { @@ -740,10 +663,10 @@ var LibrarySDL = { SDL_Linked_Version: function() { if (SDL.version === null) { - SDL.version = _malloc(SDL.structs.version.__size__); - {{{ makeSetValue('SDL.version + SDL.structs.version.major', '0', '1', 'i8') }}} - {{{ makeSetValue('SDL.version + SDL.structs.version.minor', '0', '3', 'i8') }}} - {{{ makeSetValue('SDL.version + SDL.structs.version.patch', '0', '0', 'i8') }}} + SDL.version = _malloc({{{ C_STRUCTS.SDL_version.__size__ }}}); + {{{ makeSetValue('SDL.version + ' + C_STRUCTS.SDL_version.major, '0', '1', 'i8') }}} + {{{ makeSetValue('SDL.version + ' + C_STRUCTS.SDL_version.minor, '0', '3', 'i8') }}} + {{{ makeSetValue('SDL.version + ' + C_STRUCTS.SDL_version.patch, '0', '0', 'i8') }}} } return SDL.version; }, @@ -887,7 +810,7 @@ var LibrarySDL = { // SDL_Surface has the following fields: Uint32 flags, SDL_PixelFormat *format; int w, h; Uint16 pitch; void *pixels; ... // So we have fields all of the same size, and 5 of them before us. // TODO: Use macros like in library.js - {{{ makeSetValue('surf', '5*Runtime.QUANTUM_SIZE', 'surfData.buffer', 'void*') }}}; + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.pixels, 'surfData.buffer', 'void*') }}}; if (surf == SDL.screen && Module.screenIsReadOnly && surfData.image) return 0; @@ -1131,7 +1054,10 @@ var LibrarySDL = { } else { dr = { x: 0, y: 0, w: -1, h: -1 }; } + var oldAlpha = dstData.ctx.globalAlpha; + dstData.ctx.globalAlpha = srcData.alpha/255; dstData.ctx.drawImage(srcData.canvas, sr.x, sr.y, sr.w, sr.h, dr.x, dr.y, sr.w, sr.h); + dstData.ctx.globalAlpha = oldAlpha; if (dst != SDL.screen) { // XXX As in IMG_Load, for compatibility we write out |pixels| console.log('WARNING: copying canvas data to memory for compatibility'); @@ -1454,60 +1380,240 @@ var LibrarySDL = { // SDL_Audio - // TODO fix SDL_OpenAudio, and add some tests for it. It's currently broken. SDL_OpenAudio: function(desired, obtained) { - SDL.allocateChannels(32); - - SDL.audio = { - freq: {{{ makeGetValue('desired', 'SDL.structs.AudioSpec.freq', 'i32', 0, 1) }}}, - format: {{{ makeGetValue('desired', 'SDL.structs.AudioSpec.format', 'i16', 0, 1) }}}, - channels: {{{ makeGetValue('desired', 'SDL.structs.AudioSpec.channels', 'i8', 0, 1) }}}, - samples: {{{ makeGetValue('desired', 'SDL.structs.AudioSpec.samples', 'i16', 0, 1) }}}, - callback: {{{ makeGetValue('desired', 'SDL.structs.AudioSpec.callback', 'void*', 0, 1) }}}, - userdata: {{{ makeGetValue('desired', 'SDL.structs.AudioSpec.userdata', 'void*', 0, 1) }}}, - paused: true, - timer: null - }; - - if (obtained) { - {{{ makeSetValue('obtained', 'SDL.structs.AudioSpec.freq', 'SDL.audio.freq', 'i32') }}}; // no good way for us to know if the browser can really handle this - {{{ makeSetValue('obtained', 'SDL.structs.AudioSpec.format', 33040, 'i16') }}}; // float, signed, 16-bit - {{{ makeSetValue('obtained', 'SDL.structs.AudioSpec.channels', 'SDL.audio.channels', 'i8') }}}; - {{{ makeSetValue('obtained', 'SDL.structs.AudioSpec.silence', makeGetValue('desired', 'SDL.structs.AudioSpec.silence', 'i8', 0, 1), 'i8') }}}; // unclear if browsers can provide this - {{{ makeSetValue('obtained', 'SDL.structs.AudioSpec.samples', 'SDL.audio.samples', 'i16') }}}; - {{{ makeSetValue('obtained', 'SDL.structs.AudioSpec.callback', 'SDL.audio.callback', '*') }}}; - {{{ makeSetValue('obtained', 'SDL.structs.AudioSpec.userdata', 'SDL.audio.userdata', '*') }}}; - } - - var totalSamples = SDL.audio.samples*SDL.audio.channels; - SDL.audio.bufferSize = totalSamples*2; // hardcoded 16-bit audio - SDL.audio.buffer = _malloc(SDL.audio.bufferSize); - SDL.audio.caller = function() { - Runtime.dynCall('viii', SDL.audio.callback, [SDL.audio.userdata, SDL.audio.buffer, SDL.audio.bufferSize]); - SDL.audio.pushAudio(SDL.audio.buffer, SDL.audio.bufferSize); - }; - // Mozilla Audio API. TODO: Other audio APIs try { - SDL.audio.mozOutput = new Audio(); - SDL.audio.mozOutput['mozSetup'](SDL.audio.channels, SDL.audio.freq); // use string attributes on mozOutput for closure compiler - SDL.audio.mozBuffer = new Float32Array(totalSamples); - SDL.audio.pushAudio = function(ptr, size) { - var mozBuffer = SDL.audio.mozBuffer; - for (var i = 0; i < totalSamples; i++) { - mozBuffer[i] = ({{{ makeGetValue('ptr', 'i*2', 'i16', 0, 0) }}}) / 0x8000; // hardcoded 16-bit audio, signed (TODO: reSign if not ta2?) + SDL.audio = { + freq: {{{ makeGetValue('desired', C_STRUCTS.SDL_AudioSpec.freq, 'i32', 0, 1) }}}, + format: {{{ makeGetValue('desired', C_STRUCTS.SDL_AudioSpec.format, 'i16', 0, 1) }}}, + channels: {{{ makeGetValue('desired', C_STRUCTS.SDL_AudioSpec.channels, 'i8', 0, 1) }}}, + samples: {{{ makeGetValue('desired', C_STRUCTS.SDL_AudioSpec.samples, 'i16', 0, 1) }}}, // Samples in the CB buffer per single sound channel. + callback: {{{ makeGetValue('desired', C_STRUCTS.SDL_AudioSpec.callback, 'void*', 0, 1) }}}, + userdata: {{{ makeGetValue('desired', C_STRUCTS.SDL_AudioSpec.userdata, 'void*', 0, 1) }}}, + paused: true, + timer: null + }; + // The .silence field tells the constant sample value that corresponds to the safe un-skewed silence value for the wave data. + if (SDL.audio.format == 0x0008 /*AUDIO_U8*/) { + SDL.audio.silence = 128; // Audio ranges in [0, 255], so silence is half-way in between. + } else if (SDL.audio.format == 0x8010 /*AUDIO_S16LSB*/) { + SDL.audio.silence = 0; // Signed data in range [-32768, 32767], silence is 0. + } else { + throw 'Invalid SDL audio format ' + SDL.audio.format + '!'; + } + // Round the desired audio frequency up to the next 'common' frequency value. + // Web Audio API spec states 'An implementation must support sample-rates in at least the range 22050 to 96000.' + if (SDL.audio.freq <= 0) { + throw 'Unsupported sound frequency ' + SDL.audio.freq + '!'; + } else if (SDL.audio.freq <= 22050) { + SDL.audio.freq = 22050; // Take it safe and clamp everything lower than 22kHz to that. + } else if (SDL.audio.freq <= 32000) { + SDL.audio.freq = 32000; + } else if (SDL.audio.freq <= 44100) { + SDL.audio.freq = 44100; + } else if (SDL.audio.freq <= 48000) { + SDL.audio.freq = 48000; + } else if (SDL.audio.freq <= 96000) { + SDL.audio.freq = 96000; + } else { + throw 'Unsupported sound frequency ' + SDL.audio.freq + '!'; + } + if (SDL.audio.channels == 0) { + SDL.audio.channels = 1; // In SDL both 0 and 1 mean mono. + } else if (SDL.audio.channels < 0 || SDL.audio.channels > 32) { + throw 'Unsupported number of audio channels for SDL audio: ' + SDL.audio.channels + '!'; + } else if (SDL.audio.channels != 1 && SDL.audio.channels != 2) { // Unsure what SDL audio spec supports. Web Audio spec supports up to 32 channels. + console.log('Warning: Using untested number of audio channels ' + SDL.audio.channels); + } + if (SDL.audio.samples < 1024 || SDL.audio.samples > 524288 /* arbitrary cap */) { + throw 'Unsupported audio callback buffer size ' + SDL.audio.samples + '!'; + } else if ((SDL.audio.samples & (SDL.audio.samples-1)) != 0) { + throw 'Audio callback buffer size ' + SDL.audio.samples + ' must be a power-of-two!'; + } + + var totalSamples = SDL.audio.samples*SDL.audio.channels; + SDL.audio.bytesPerSample = (SDL.audio.format == 0x0008 /*AUDIO_U8*/ || SDL.audio.format == 0x8008 /*AUDIO_S8*/) ? 1 : 2; + SDL.audio.bufferSize = totalSamples*SDL.audio.bytesPerSample; + SDL.audio.buffer = _malloc(SDL.audio.bufferSize); + + // Create a callback function that will be routinely called to ask more audio data from the user application. + SDL.audio.caller = function() { + if (!SDL.audio) { + return; } - SDL.audio.mozOutput['mozWriteAudio'](mozBuffer); + Runtime.dynCall('viii', SDL.audio.callback, [SDL.audio.userdata, SDL.audio.buffer, SDL.audio.bufferSize]); + SDL.audio.pushAudio(SDL.audio.buffer, SDL.audio.bufferSize); + }; + + SDL.audio.audioOutput = new Audio(); + // As a workaround use Mozilla Audio Data API on Firefox until it ships with Web Audio and sound quality issues are fixed. + if (typeof(SDL.audio.audioOutput['mozSetup'])==='function') { + SDL.audio.audioOutput['mozSetup'](SDL.audio.channels, SDL.audio.freq); // use string attributes on mozOutput for closure compiler + SDL.audio.mozBuffer = new Float32Array(totalSamples); + SDL.audio.nextPlayTime = 0; + SDL.audio.pushAudio = function(ptr, size) { + var mozBuffer = SDL.audio.mozBuffer; + // The input audio data for SDL audio is either 8-bit or 16-bit interleaved across channels, output for Mozilla Audio Data API + // needs to be Float32 interleaved, so perform a sample conversion. + if (SDL.audio.format == 0x8010 /*AUDIO_S16LSB*/) { + for (var i = 0; i < totalSamples; i++) { + mozBuffer[i] = ({{{ makeGetValue('ptr', 'i*2', 'i16', 0, 0) }}}) / 0x8000; + } + } else if (SDL.audio.format == 0x0008 /*AUDIO_U8*/) { + for (var i = 0; i < totalSamples; i++) { + var v = ({{{ makeGetValue('ptr', 'i', 'i8', 0, 0) }}}); + mozBuffer[i] = ((v >= 0) ? v-128 : v+128) /128; + } + } + // Submit the audio data to audio device. + SDL.audio.audioOutput['mozWriteAudio'](mozBuffer); + + // Compute when the next audio callback should be called. + var curtime = Date.now() / 1000.0 - SDL.audio.startTime; + if (curtime > SDL.audio.nextPlayTime && SDL.audio.nextPlayTime != 0) { + console.log('warning: Audio callback had starved sending audio by ' + (curtime - SDL.audio.nextPlayTime) + ' seconds.'); + } + var playtime = Math.max(curtime, SDL.audio.nextPlayTime); + var buffer_duration = SDL.audio.samples / SDL.audio.freq; + SDL.audio.nextPlayTime = playtime + buffer_duration; + // Schedule the next audio callback call. + SDL.audio.timer = Browser.safeSetTimeout(SDL.audio.caller, 1000.0 * (playtime-curtime)); + } + } else { + // Initialize Web Audio API if we haven't done so yet. Note: Only initialize Web Audio context ever once on the web page, + // since initializing multiple times fails on Chrome saying 'audio resources have been exhausted'. + if (!SDL.audioContext) { + if (typeof(AudioContext) === 'function') { + SDL.audioContext = new AudioContext(); + } else if (typeof(webkitAudioContext) === 'function') { + SDL.audioContext = new webkitAudioContext(); + } else { + throw 'Web Audio API is not available!'; + } + } + SDL.audio.soundSource = new Array(); // Use an array of sound sources as a ring buffer to queue blocks of synthesized audio to Web Audio API. + SDL.audio.nextSoundSource = 0; // Index of the next sound buffer in the ring buffer queue to play. + SDL.audio.nextPlayTime = 0; // Time in seconds when the next audio block is due to start. + + // The pushAudio function with a new audio buffer whenever there is new audio data to schedule to be played back on the device. + SDL.audio.pushAudio=function(ptr,sizeBytes) { + try { + --SDL.audio.numAudioTimersPending; + + var sizeSamples = sizeBytes / SDL.audio.bytesPerSample; // How many samples fit in the callback buffer? + var sizeSamplesPerChannel = sizeSamples / SDL.audio.channels; // How many samples per a single channel fit in the cb buffer? + if (sizeSamplesPerChannel != SDL.audio.samples) { + throw 'Received mismatching audio buffer size!'; + } + // Allocate new sound buffer to be played. + var source = SDL.audioContext['createBufferSource'](); + if (SDL.audio.soundSource[SDL.audio.nextSoundSource]) { + SDL.audio.soundSource[SDL.audio.nextSoundSource]['disconnect'](); // Explicitly disconnect old source, since we know it shouldn't be running anymore. + } + SDL.audio.soundSource[SDL.audio.nextSoundSource] = source; + var soundBuffer = SDL.audioContext['createBuffer'](SDL.audio.channels,sizeSamplesPerChannel,SDL.audio.freq); + SDL.audio.soundSource[SDL.audio.nextSoundSource]['connect'](SDL.audioContext['destination']); + + // The input audio data is interleaved across the channels, i.e. [L, R, L, R, L, R, ...] and is either 8-bit or 16-bit as + // supported by the SDL API. The output audio wave data for Web Audio API must be in planar buffers of [-1,1]-normalized Float32 data, + // so perform a buffer conversion for the data. + var numChannels = SDL.audio.channels; + for(var i = 0; i < numChannels; ++i) { + var channelData = soundBuffer['getChannelData'](i); + if (channelData.length != sizeSamplesPerChannel) { + throw 'Web Audio output buffer length mismatch! Destination size: ' + channelData.length + ' samples vs expected ' + sizeSamplesPerChannel + ' samples!'; + } + if (SDL.audio.format == 0x8010 /*AUDIO_S16LSB*/) { + for(var j = 0; j < sizeSamplesPerChannel; ++j) { + channelData[j] = ({{{ makeGetValue('ptr', '(j*numChannels + i)*2', 'i16', 0, 0) }}}) / 0x8000; + } + } else if (SDL.audio.format == 0x0008 /*AUDIO_U8*/) { + for(var j = 0; j < sizeSamplesPerChannel; ++j) { + var v = ({{{ makeGetValue('ptr', 'j*numChannels + i', 'i8', 0, 0) }}}); + channelData[j] = ((v >= 0) ? v-128 : v+128) /128; + } + } + } + // Workaround https://bugzilla.mozilla.org/show_bug.cgi?id=883675 by setting the buffer only after filling. The order is important here! + source['buffer'] = soundBuffer; + + // Schedule the generated sample buffer to be played out at the correct time right after the previously scheduled + // sample buffer has finished. + var curtime = SDL.audioContext['currentTime']; +// if (curtime > SDL.audio.nextPlayTime && SDL.audio.nextPlayTime != 0) { +// console.log('warning: Audio callback had starved sending audio by ' + (curtime - SDL.audio.nextPlayTime) + ' seconds.'); +// } + var playtime = Math.max(curtime, SDL.audio.nextPlayTime); + SDL.audio.soundSource[SDL.audio.nextSoundSource]['start'](playtime); + var buffer_duration = sizeSamplesPerChannel / SDL.audio.freq; + SDL.audio.nextPlayTime = playtime + buffer_duration; + SDL.audio.nextSoundSource = (SDL.audio.nextSoundSource + 1) % 4; + var secsUntilNextCall = playtime-curtime; + + // Queue the next audio frame push to be performed when the previously queued buffer has finished playing. + if (SDL.audio.numAudioTimersPending == 0) { + var preemptBufferFeedMSecs = buffer_duration/2.0; + SDL.audio.timer = Browser.safeSetTimeout(SDL.audio.caller, Math.max(0.0, 1000.0*secsUntilNextCall-preemptBufferFeedMSecs)); + ++SDL.audio.numAudioTimersPending; + } + + // If we are risking starving, immediately queue an extra second buffer. + if (secsUntilNextCall <= buffer_duration && SDL.audio.numAudioTimersPending <= 1) { + ++SDL.audio.numAudioTimersPending; + Browser.safeSetTimeout(SDL.audio.caller, 1.0); + } + } catch(e) { + console.log('Web Audio API error playing back audio: ' + e.toString()); + } + } + } + + if (obtained) { + // Report back the initialized audio parameters. + {{{ makeSetValue('obtained', C_STRUCTS.SDL_AudioSpec.freq, 'SDL.audio.freq', 'i32') }}}; + {{{ makeSetValue('obtained', C_STRUCTS.SDL_AudioSpec.format, 'SDL.audio.format', 'i16') }}}; + {{{ makeSetValue('obtained', C_STRUCTS.SDL_AudioSpec.channels, 'SDL.audio.channels', 'i8') }}}; + {{{ makeSetValue('obtained', C_STRUCTS.SDL_AudioSpec.silence, 'SDL.audio.silence', 'i8') }}}; + {{{ makeSetValue('obtained', C_STRUCTS.SDL_AudioSpec.samples, 'SDL.audio.samples', 'i16') }}}; + {{{ makeSetValue('obtained', C_STRUCTS.SDL_AudioSpec.callback, 'SDL.audio.callback', '*') }}}; + {{{ makeSetValue('obtained', C_STRUCTS.SDL_AudioSpec.userdata, 'SDL.audio.userdata', '*') }}}; } + SDL.allocateChannels(32); + } catch(e) { + console.log('Initializing SDL audio threw an exception: "' + e.toString() + '"! Continuing without audio.'); SDL.audio = null; + SDL.allocateChannels(0); + if (obtained) { + {{{ makeSetValue('obtained', C_STRUCTS.SDL_AudioSpec.freq, 0, 'i32') }}}; + {{{ makeSetValue('obtained', C_STRUCTS.SDL_AudioSpec.format, 0, 'i16') }}}; + {{{ makeSetValue('obtained', C_STRUCTS.SDL_AudioSpec.channels, 0, 'i8') }}}; + {{{ makeSetValue('obtained', C_STRUCTS.SDL_AudioSpec.silence, 0, 'i8') }}}; + {{{ makeSetValue('obtained', C_STRUCTS.SDL_AudioSpec.samples, 0, 'i16') }}}; + {{{ makeSetValue('obtained', C_STRUCTS.SDL_AudioSpec.callback, 0, '*') }}}; + {{{ makeSetValue('obtained', C_STRUCTS.SDL_AudioSpec.userdata, 0, '*') }}}; + } + } + if (!SDL.audio) { + return -1; } - if (!SDL.audio) return -1; return 0; }, SDL_PauseAudio: function(pauseOn) { - if (SDL.audio.paused !== pauseOn) { - SDL.audio.timer = pauseOn ? SDL.audio.timer && clearInterval(SDL.audio.timer) : Browser.safeSetInterval(SDL.audio.caller, 1/35); + if (!SDL.audio) { + return; + } + if (pauseOn) { + if (SDL.audio.timer !== undefined) { + clearTimeout(SDL.audio.timer); + SDL.audio.numAudioTimersPending = 0; + SDL.audio.timer = undefined; + } + } else if (!SDL.audio.timer) { + // Start the audio playback timer callback loop. + SDL.audio.numAudioTimersPending = 1; + SDL.audio.timer = Browser.safeSetTimeout(SDL.audio.caller, 1); + SDL.audio.startTime = Date.now() / 1000.0; // Only used for Mozilla Audio Data API. Not needed for Web Audio API. } SDL.audio.paused = pauseOn; }, @@ -1515,9 +1621,18 @@ var LibrarySDL = { SDL_CloseAudio__deps: ['SDL_PauseAudio', 'free'], SDL_CloseAudio: function() { if (SDL.audio) { + try{ + for(var i = 0; i < SDL.audio.soundSource.length; ++i) { + if (!(typeof(SDL.audio.soundSource[i]==='undefined'))) { + SDL.audio.soundSource[i].stop(0); + } + } + } catch(e) {} + SDL.audio.soundSource = null; _SDL_PauseAudio(1); _free(SDL.audio.buffer); SDL.audio = null; + SDL.allocateChannels(0); } }, diff --git a/src/library_sockfs.js b/src/library_sockfs.js index b11c6495..af29d11b 100644 --- a/src/library_sockfs.js +++ b/src/library_sockfs.js @@ -5,12 +5,6 @@ mergeInto(LibraryManager.library, { mount: function(mount) { return FS.createNode(null, '/', {{{ cDefine('S_IFDIR') }}} | 0777, 0); }, - nextname: function() { - if (!SOCKFS.nextname.current) { - SOCKFS.nextname.current = 0; - } - return 'socket[' + (SOCKFS.nextname.current++) + ']'; - }, createSocket: function(family, type, protocol) { var streaming = type == {{{ cDefine('SOCK_STREAM') }}}; if (protocol) { @@ -95,6 +89,12 @@ mergeInto(LibraryManager.library, { sock.sock_ops.close(sock); } }, + nextname: function() { + if (!SOCKFS.nextname.current) { + SOCKFS.nextname.current = 0; + } + return 'socket[' + (SOCKFS.nextname.current++) + ']'; + }, // backend-specific stream ops websocket_sock_ops: { // diff --git a/src/modules.js b/src/modules.js index 1029b233..cc9ca549 100644 --- a/src/modules.js +++ b/src/modules.js @@ -234,7 +234,7 @@ var Types = { preciseI64MathUsed: (PRECISE_I64_MATH == 2) }; -var firstTableIndex = (ASM_JS ? 2*RESERVED_FUNCTION_POINTERS : 0) + 2; +var firstTableIndex = FUNCTION_POINTER_ALIGNMENT * ((ASM_JS ? RESERVED_FUNCTION_POINTERS : 0) + 1); var Functions = { // All functions that will be implemented in this file. Maps id to signature @@ -287,7 +287,7 @@ var Functions = { ret = this.indexedFunctions[ident]; if (!ret) { ret = this.nextIndex; - this.nextIndex += 2; // Need to have indexes be even numbers, see |polymorph| test + this.nextIndex += FUNCTION_POINTER_ALIGNMENT; this.indexedFunctions[ident] = ret; } ret = ret.toString(); @@ -340,7 +340,7 @@ var Functions = { if (table[i]) { var libName = LibraryManager.getRootIdent(table[i].substr(1)); if (libName && typeof libName == 'string') { - table[i] = (libName.indexOf('.') < 0 ? '_' : '') + libName; + table[i] = (libName.indexOf('Math_') < 0 ? '_' : '') + libName; } } if (ASM_JS) { @@ -373,27 +373,17 @@ var Functions = { } } } - if (table.length > 20) { - // add some newlines in the table, for readability - var j = 10; - while (j+10 < table.length) { - table[j] += '\n'; - j += 10; - } - } maxTable = Math.max(maxTable, table.length); } if (ASM_JS) maxTable = ceilPowerOfTwo(maxTable); for (var t in tables) { if (t == 'pre') continue; var table = tables[t]; - if (ASM_JS) { - // asm function table mask must be power of two - // if nonaliasing, then standardize function table size, to avoid aliasing pointers through the &M mask (in a small table using a big index) - var fullSize = ALIASING_FUNCTION_POINTERS ? ceilPowerOfTwo(table.length) : maxTable; - for (var i = table.length; i < fullSize; i++) { - table[i] = 0; - } + // asm function table mask must be power of two, and non-asm must be aligned + // if nonaliasing, then standardize function table size, to avoid aliasing pointers through the &M mask (in a small table using a big index) + var fullSize = ASM_JS ? (ALIASING_FUNCTION_POINTERS ? ceilPowerOfTwo(table.length) : maxTable) : ((table.length+FUNCTION_POINTER_ALIGNMENT-1)&-FUNCTION_POINTER_ALIGNMENT); + for (var i = table.length; i < fullSize; i++) { + table[i] = 0; } // finalize table var indices = table.toString().replace('"', ''); @@ -422,7 +412,7 @@ var LibraryManager = { load: function() { if (this.library) return; - var libraries = ['library.js', 'library_path.js', 'library_fs.js', 'library_memfs.js', 'library_sockfs.js', 'library_tty.js', 'library_browser.js', 'library_sdl.js', 'library_gl.js', 'library_glut.js', 'library_xlib.js', 'library_egl.js', 'library_gc.js', 'library_jansson.js', 'library_openal.js', 'library_glfw.js'].concat(additionalLibraries); + var libraries = ['library.js', 'library_path.js', 'library_fs.js', 'library_idbfs.js', 'library_memfs.js', 'library_nodefs.js', 'library_sockfs.js', 'library_tty.js', 'library_browser.js', 'library_sdl.js', 'library_gl.js', 'library_glut.js', 'library_xlib.js', 'library_egl.js', 'library_gc.js', 'library_jansson.js', 'library_openal.js', 'library_glfw.js'].concat(additionalLibraries); for (var i = 0; i < libraries.length; i++) { eval(processMacros(preprocess(read(libraries[i])))); } diff --git a/src/parseTools.js b/src/parseTools.js index c55c895d..addf0f21 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -5,11 +5,12 @@ // Does simple 'macro' substitution, using Django-like syntax, // {{{ code }}} will be replaced with |eval(code)|. +// NOTE: Be careful with that ret check. If ret is |0|, |ret ? ret.toString() : ''| would result in ''! function processMacros(text) { return text.replace(/{{{([^}]|}(?!}))+}}}/g, function(str) { str = str.substr(3, str.length-6); var ret = eval(str); - return ret ? ret.toString() : ''; + return ret !== null ? ret.toString() : ''; }); } @@ -110,12 +111,22 @@ function isNiceIdent(ident, loose) { } function isJSVar(ident) { - return /^\(?[$_]?[\w$_\d ]*\)+$/.test(ident); - + if (ident[0] === '(') { + if (ident[ident.length-1] !== ')') return false; + ident = ident.substr(1, ident.length-2); + } + return /^[$_]?[\w$_\d]* *$/.test(ident); } function isLocalVar(ident) { - return ident[0] == '$'; + return ident[0] === '$'; +} + +// Simple variables or numbers, or things already quoted, do not need to be quoted +function needsQuoting(ident) { + if (/^[-+]?[$_]?[\w$_\d]*$/.test(ident)) return false; // number or variable + if (ident[0] === '(' && ident[ident.length-1] === ')' && ident.indexOf('(', 1) < 0) return false; // already fully quoted + return true; } function isStructPointerType(type) { @@ -752,10 +763,10 @@ function splitI64(value, floatConversion) { if (floatConversion && ASM_JS) lowInput = asmFloatToInt(lowInput); var low = lowInput + '>>>0'; var high = makeInlineCalculation( - asmCoercion('Math.abs(VALUE)', 'double') + ' >= ' + asmEnsureFloat('1', 'double') + ' ? ' + + asmCoercion('Math_abs(VALUE)', 'double') + ' >= ' + asmEnsureFloat('1', 'double') + ' ? ' + '(VALUE > ' + asmEnsureFloat('0', 'double') + ' ? ' + - asmCoercion('Math.min(' + asmCoercion('Math.floor((VALUE)/' + asmEnsureFloat(4294967296, 'float') + ')', 'double') + ', ' + asmEnsureFloat(4294967295, 'float') + ')', 'i32') + '>>>0' + - ' : ' + asmFloatToInt(asmCoercion('Math.ceil((VALUE - +((' + asmFloatToInt('VALUE') + ')>>>0))/' + asmEnsureFloat(4294967296, 'float') + ')', 'double')) + '>>>0' + + asmCoercion('Math_min(' + asmCoercion('Math_floor((VALUE)/' + asmEnsureFloat(4294967296, 'float') + ')', 'double') + ', ' + asmEnsureFloat(4294967295, 'float') + ')', 'i32') + '>>>0' + + ' : ' + asmFloatToInt(asmCoercion('Math_ceil((VALUE - +((' + asmFloatToInt('VALUE') + ')>>>0))/' + asmEnsureFloat(4294967296, 'float') + ')', 'double')) + '>>>0' + ')' + ' : 0', value, @@ -919,7 +930,10 @@ function parseNumerical(value, type) { } if (isNumber(value)) { var ret = parseFloat(value); // will change e.g. 5.000000e+01 to 50 - if (type in Runtime.FLOAT_TYPES && value[0] == '-' && ret === 0) return '-0'; // fix negative 0, toString makes it 0 + if (type in Runtime.FLOAT_TYPES) { + if (value[0] === '-' && ret === 0) return '-.0'; // fix negative 0, toString makes it 0 + if (!RUNNING_JS_OPTS) ret = asmEnsureFloat(ret, type); + } return ret.toString(); } else { return value; @@ -932,12 +946,12 @@ function parseLLVMString(str) { var ret = []; var i = 0; while (i < str.length) { - var chr = str[i]; - if (chr != '\\') { - ret.push(chr.charCodeAt(0)); + var chr = str.charCodeAt(i); + if (chr !== 92) { // 92 === '//'.charCodeAt(0) + ret.push(chr); i++; } else { - ret.push(eval('0x' + str[i+1]+str[i+2])); + ret.push(parseInt(str[i+1]+str[i+2], '16')); i += 3; } } @@ -1125,7 +1139,16 @@ function asmEnsureFloat(value, type) { // ensures that a float type has either 5 if (!ASM_JS) return value; // coerce if missing a '.', or if smaller than 1, so could be 1e-5 which has no . if (type in Runtime.FLOAT_TYPES && isNumber(value) && (value.toString().indexOf('.') < 0 || Math.abs(value) < 1)) { - return '(+(' + value + '))'; + if (RUNNING_JS_OPTS) { + return '(+' + value + ')'; // JS optimizer will run, we must do +x, and it will be corrected later + } else { + // ensure a . + value = value.toString(); + if (value.indexOf('.') >= 0 || /[IN]/.test(value)) return value; // if already dotted, or Infinity or NaN, nothing to do here + var e = value.indexOf('e'); + if (e < 0) return value + '.0'; + return value.substr(0, e) + '.0' + value.substr(e); + } } else { return value; } @@ -1133,7 +1156,11 @@ function asmEnsureFloat(value, type) { // ensures that a float type has either 5 function asmInitializer(type, impl) { if (type in Runtime.FLOAT_TYPES) { - return '+0'; + if (RUNNING_JS_OPTS) { + return '+0'; + } else { + return '.0'; + } } else { return '0'; } @@ -1348,6 +1375,7 @@ function makeSetValue(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe, value = indexizeFunctions(value, type); var offset = calcFastOffset(ptr, pos, noNeedFirst); + if (phase === 'pre' && isNumber(offset)) offset += ' '; // avoid pure numeric strings, seem to be perf issues with overly-aggressive interning or slt in pre processing of heap inits if (SAFE_HEAP && !noSafe) { var printType = type; if (printType !== 'null' && printType[0] !== '#') printType = '"' + safeQuote(printType) + '"'; @@ -1464,77 +1492,97 @@ function makeHEAPView(which, start, end) { return 'HEAP' + which + '.subarray((' + start + ')' + mod + ',(' + end + ')' + mod + ')'; } -var PLUS_MUL = set('+', '*'); -var MUL_DIV = set('*', '/'); -var PLUS_MINUS = set('+', '-'); var TWO_TWENTY = Math.pow(2, 20); // Given two values and an operation, returns the result of that operation. // Tries to do as much as possible at compile time. // Leaves overflows etc. unhandled, *except* for integer multiply, in order to be efficient with Math.imul function getFastValue(a, op, b, type) { - a = a.toString(); - b = b.toString(); - a = a == 'true' ? '1' : (a == 'false' ? '0' : a); - b = b == 'true' ? '1' : (b == 'false' ? '0' : b); - if (isNumber(a) && isNumber(b)) { - if (op == 'pow') { - return Math.pow(a, b).toString(); - } else { - var value = eval(a + op + '(' + b + ')'); // parens protect us from "5 - -12" being seen as "5--12" which is "(5--)12" - if (op == '/' && type in Runtime.INT_TYPES) value = value|0; // avoid emitting floats - return value.toString(); + a = a === 'true' ? '1' : (a === 'false' ? '0' : a); + b = b === 'true' ? '1' : (b === 'false' ? '0' : b); + + var aNumber = null, bNumber = null; + if (typeof a === 'number') { + aNumber = a; + a = a.toString(); + } else if (isNumber(a)) aNumber = parseFloat(a); + if (typeof b === 'number') { + bNumber = b; + b = b.toString(); + } else if (isNumber(b)) bNumber = parseFloat(b); + + if (aNumber !== null && bNumber !== null) { + switch (op) { + case '+': return (aNumber + bNumber).toString(); + case '-': return (aNumber - bNumber).toString(); + case '*': return (aNumber * bNumber).toString(); + case '/': { + if (type[0] === 'i') { + return ((aNumber / bNumber)|0).toString(); + } else { + return (aNumber / bNumber).toString(); + } + } + case '%': return (aNumber % bNumber).toString(); + case '|': return (aNumber | bNumber).toString(); + case '>>>': return (aNumber >>> bNumber).toString(); + case '&': return (aNumber & bNumber).toString(); + case 'pow': return Math.pow(aNumber, bNumber).toString(); + default: throw 'need to implement getFastValue pn ' + op; } } - if (op == 'pow') { - if (a == '2' && isIntImplemented(type)) { + if (op === 'pow') { + if (a === '2' && isIntImplemented(type)) { return '(1 << (' + b + '))'; } - return 'Math.pow(' + a + ', ' + b + ')'; + return 'Math_pow(' + a + ', ' + b + ')'; } - if (op in PLUS_MUL && isNumber(a)) { // if one of them is a number, keep it last + if ((op === '+' || op === '*') && aNumber !== null) { // if one of them is a number, keep it last var c = b; b = a; a = c; - } - if (op in MUL_DIV) { - if (op == '*') { - if (a == 0 || b == 0) { - return '0'; - } else if (a == 1) { - return b; - } else if (b == 1) { - return a; - } else if (isNumber(b) && type && isIntImplemented(type) && Runtime.getNativeTypeSize(type) <= 32) { - var shifts = Math.log(parseFloat(b))/Math.LN2; - if (shifts % 1 == 0) { - return '(' + a + '<<' + shifts + ')'; - } + var cNumber = bNumber; + bNumber = aNumber; + aNumber = cNumber; + } + if (op === '*') { + // We can't eliminate where a or b are 0 as that would break things for creating + // a negative 0. + if ((aNumber === 0 || bNumber === 0) && !(type in Runtime.FLOAT_TYPES)) { + return '0'; + } else if (aNumber === 1) { + return b; + } else if (bNumber === 1) { + return a; + } else if (bNumber !== null && type && isIntImplemented(type) && Runtime.getNativeTypeSize(type) <= 32) { + var shifts = Math.log(bNumber)/Math.LN2; + if (shifts % 1 === 0) { + return '(' + a + '<<' + shifts + ')'; } - if (!(type in Runtime.FLOAT_TYPES)) { - // if guaranteed small enough to not overflow into a double, do a normal multiply - var bits = getBits(type) || 32; // default is 32-bit multiply for things like getelementptr indexes - // Note that we can emit simple multiple in non-asm.js mode, but asm.js will not parse "16-bit" multiple, so must do imul there - if ((isNumber(a) && Math.abs(a) < TWO_TWENTY) || (isNumber(b) && Math.abs(b) < TWO_TWENTY) || (bits < 32 && !ASM_JS)) { - return '(((' + a + ')*(' + b + '))&' + ((Math.pow(2, bits)-1)|0) + ')'; // keep a non-eliminatable coercion directly on this - } - return '(Math.imul(' + a + ',' + b + ')|0)'; + } + if (!(type in Runtime.FLOAT_TYPES)) { + // if guaranteed small enough to not overflow into a double, do a normal multiply + var bits = getBits(type) || 32; // default is 32-bit multiply for things like getelementptr indexes + // Note that we can emit simple multiple in non-asm.js mode, but asm.js will not parse "16-bit" multiple, so must do imul there + if ((aNumber !== null && Math.abs(a) < TWO_TWENTY) || (bNumber !== null && Math.abs(b) < TWO_TWENTY) || (bits < 32 && !ASM_JS)) { + return '(((' + a + ')*(' + b + '))&' + ((Math.pow(2, bits)-1)|0) + ')'; // keep a non-eliminatable coercion directly on this } - } else { - if (a == '0') { - return '0'; - } else if (b == 1) { - return a; - } // Doing shifts for division is problematic, as getting the rounding right on negatives is tricky - } - } else if (op in PLUS_MINUS) { - if (b[0] == '-') { - op = op == '+' ? '-' : '+'; + return '(Math_imul(' + a + ',' + b + ')|0)'; + } + } else if (op === '/') { + if (a === '0' && !(type in Runtime.FLOAT_TYPES)) { // careful on floats, since 0*NaN is not 0 + return '0'; + } else if (b === 1) { + return a; + } // Doing shifts for division is problematic, as getting the rounding right on negatives is tricky + } else if (op === '+' || op === '-') { + if (b[0] === '-') { + op = op === '+' ? '-' : '+'; b = b.substr(1); } - if (a == 0) { - return op == '+' ? b : '(-' + b + ')'; - } else if (b == 0) { + if (aNumber === 0) { + return op === '+' ? b : '(-' + b + ')'; + } else if (bNumber === 0) { return a; } } @@ -1563,12 +1611,8 @@ function getFastValues(list, op, type) { } function calcFastOffset(ptr, pos, noNeedFirst) { - var offset = noNeedFirst ? '0' : makeGetPos(ptr); - return getFastValue(offset, '+', pos, 'i32'); -} - -function makeGetPos(ptr) { - return ptr; + assert(!noNeedFirst); + return getFastValue(ptr, '+', pos, 'i32'); } var IHEAP_FHEAP = set('IHEAP', 'IHEAPU', 'FHEAP'); @@ -1758,7 +1802,7 @@ function checkBitcast(item) { } else { warnOnce('Casting a function pointer type to a potentially incompatible one (use -s VERBOSE=1 to see more)'); } - warnOnce('See https://github.com/kripken/emscripten/wiki/CodeGuidlinesAndLimitations#function-pointer-issues for more information on dangerous function pointer casts'); + warnOnce('See https://github.com/kripken/emscripten/wiki/CodeGuidelinesAndLimitations#function-pointer-issues for more information on dangerous function pointer casts'); if (ASM_JS) warnOnce('Incompatible function pointer casts are very dangerous with ASM_JS=1, you should investigate and correct these'); } if (oldCount != newCount && oldCount && newCount) showWarning(); @@ -1805,7 +1849,7 @@ function getGetElementPtrIndexes(item) { // struct, and possibly further substructures, all embedded // can also be to 'blocks': [8 x i32]*, not just structs type = removePointing(type); - var indexes = [makeGetPos(ident)]; + var indexes = [ident]; var offset = item.params[1]; if (offset != 0) { if (isStructType(type)) { @@ -1860,8 +1904,10 @@ function handleOverflow(text, bits) { if (CHECK_OVERFLOWS) return 'CHECK_OVERFLOW(' + text + ', ' + bits + ', ' + Math.floor(correctSpecificOverflow()) + ')'; if (!correct) return text; if (bits == 32) { + if (isNumber(text)) return text | 0; return '((' + text + ')|0)'; } else if (bits < 32) { + if (isNumber(text)) return text & (Math.pow(2, bits) - 1); return '((' + text + ')&' + (Math.pow(2, bits) - 1) + ')'; } else { return text; // We warned about this earlier @@ -1951,7 +1997,7 @@ function makeComparison(a, op, b, type) { return asmCoercion(a, type) + op + asmCoercion(b, type); } else { assert(type == 'i64'); - return asmCoercion(a + '$0', 'i32') + op + asmCoercion(b + '$0', 'i32') + ' & ' + + return asmCoercion(a + '$0', 'i32') + op + asmCoercion(b + '$0', 'i32') + '&' + asmCoercion(a + '$1', 'i32') + op + asmCoercion(b + '$1', 'i32'); } } @@ -1960,13 +2006,12 @@ function makeSignOp(value, type, op, force, ignore) { if (USE_TYPED_ARRAYS == 2 && type == 'i64') { return value; // these are always assumed to be two 32-bit unsigneds. } - if (isPointerType(type)) type = 'i32'; // Pointers are treated as 32-bit ints if (!value) return value; var bits, full; if (type in Runtime.INT_TYPES) { bits = parseInt(type.substr(1)); - full = op + 'Sign(' + value + ', ' + bits + ', ' + Math.floor(ignore || (correctSpecificSign())) + ')'; + full = op + 'Sign(' + value + ', ' + bits + ', ' + Math.floor(ignore || correctSpecificSign()) + ')'; // Always sign/unsign constants at compile time, regardless of CHECK/CORRECT if (isNumber(value)) { return eval(full).toString(); @@ -1974,23 +2019,25 @@ function makeSignOp(value, type, op, force, ignore) { } if ((ignore || !correctSigns()) && !CHECK_SIGNS && !force) return value; if (type in Runtime.INT_TYPES) { + // this is an integer, but not a number (or we would have already handled it) // shortcuts if (!CHECK_SIGNS || ignore) { + if (value === 'true') { + value = '1'; + } else if (value === 'false') { + value = '0'; + } else if (needsQuoting(value)) value = '(' + value + ')'; if (bits === 32) { if (op === 're') { - return '(' + getFastValue(value, '|', '0') + ')'; + return '(' + value + '|0)'; } else { - - return '(' + getFastValue(value, '>>>', '0') + ')'; - // Alternatively, we can consider the lengthier - // return makeInlineCalculation('VALUE >= 0 ? VALUE : ' + Math.pow(2, bits) + ' + VALUE', value, 'tempBigInt'); - // which does not always turn us into a 32-bit *un*signed value + return '(' + value + '>>>0)'; } } else if (bits < 32) { if (op === 're') { - return makeInlineCalculation('(VALUE << ' + (32-bits) + ') >> ' + (32-bits), value, 'tempInt'); + return '((' + value + '<<' + (32-bits) + ')>>' + (32-bits) + ')'; } else { - return '(' + getFastValue(value, '&', Math.pow(2, bits)-1) + ')'; + return '(' + value + '&' + (Math.pow(2, bits)-1) + ')'; } } else { // bits > 32 if (op === 're') { @@ -2018,12 +2065,12 @@ function makeRounding(value, bits, signed, floatConversion) { // as |0, but &-1 hints to the js optimizer that this is a rounding correction // Do Math.floor, which is reasonably fast, if we either don't care, or if we can be sure // the value is non-negative - if (!correctRoundings() || (!signed && !floatConversion)) return 'Math.floor(' + value + ')'; + if (!correctRoundings() || (!signed && !floatConversion)) return 'Math_floor(' + value + ')'; // We are left with >32 bits signed, or a float conversion. Check and correct inline // Note that if converting a float, we may have the wrong sign at this point! But, we have // been rounded properly regardless, and we will be sign-corrected later when actually used, if // necessary. - return makeInlineCalculation(makeComparison('VALUE', '>=', '0', 'float') + ' ? Math.floor(VALUE) : Math.ceil(VALUE)', value, 'tempBigIntR'); + return makeInlineCalculation(makeComparison('VALUE', '>=', '0', 'float') + ' ? Math_floor(VALUE) : Math_ceil(VALUE)', value, 'tempBigIntR'); } else { // asm.js mode, cleaner refactoring of this function as well. TODO: use in non-asm case, most of this if (floatConversion && bits <= 32) { @@ -2038,9 +2085,9 @@ function makeRounding(value, bits, signed, floatConversion) { } } // Math.floor is reasonably fast if we don't care about corrections (and even correct if unsigned) - if (!correctRoundings() || !signed) return 'Math.floor(' + value + ')'; + if (!correctRoundings() || !signed) return 'Math_floor(' + value + ')'; // We are left with >32 bits - return makeInlineCalculation(makeComparison('VALUE', '>=', '0', 'float') + ' ? Math.floor(VALUE) : Math.ceil(VALUE)', value, 'tempBigIntR'); + return makeInlineCalculation(makeComparison('VALUE', '>=', '0', 'float') + ' ? Math_floor(VALUE) : Math_ceil(VALUE)', value, 'tempBigIntR'); } } @@ -2051,7 +2098,7 @@ function makeIsNaN(value) { function makeFloat(value, type) { if (TO_FLOAT32 && type == 'float') { - return 'Math.toFloat32(' + value + ')'; + return 'Math_toFloat32(' + value + ')'; } return value; } @@ -2080,7 +2127,7 @@ function processMathop(item) { if (item.params[i]) { paramTypes[i] = item.params[i].type || type; idents[i] = finalizeLLVMParameter(item.params[i]); - if (!isNumber(idents[i]) && !isNiceIdent(idents[i])) { + if (needsQuoting(idents[i])) { idents[i] = '(' + idents[i] + ')'; // we may have nested expressions. So enforce the order of operations we want } } else { @@ -2127,7 +2174,13 @@ function processMathop(item) { // If this is in legalization mode, steal the assign and assign into two vars if (legalizedI64s) { assert(item.assignTo); - var ret = 'var ' + item.assignTo + '$0 = ' + result[0] + '; var ' + item.assignTo + '$1 = ' + result[1] + ';'; + if (ASM_JS) { + var ret = item.assignTo + '$0=' + result[0] + ';' + item.assignTo + '$1=' + result[1] + ';'; + addVariable(item.assignTo + '$0', 'i32'); + addVariable(item.assignTo + '$1', 'i32'); + } else { + var ret = 'var ' + item.assignTo + '$0=' + result[0] + ';var ' + item.assignTo + '$1=' + result[1] + ';'; + } item.assignTo = null; return ret; } else { @@ -2278,7 +2331,7 @@ function processMathop(item) { dprint('Warning: 64 bit OR - precision limit may be hit on llvm line ' + item.lineNum); return 'Runtime.or64(' + idents[0] + ', ' + idents[1] + ')'; } - return idents[0] + ' | ' + idents[1]; + return idents[0] + '|' + idents[1]; } case 'and': { if (bits > 32) { @@ -2286,7 +2339,7 @@ function processMathop(item) { dprint('Warning: 64 bit AND - precision limit may be hit on llvm line ' + item.lineNum); return 'Runtime.and64(' + idents[0] + ', ' + idents[1] + ')'; } - return idents[0] + ' & ' + idents[1]; + return idents[0] + '&' + idents[1]; } case 'xor': { if (bits > 32) { @@ -2294,21 +2347,21 @@ function processMathop(item) { dprint('Warning: 64 bit XOR - precision limit may be hit on llvm line ' + item.lineNum); return 'Runtime.xor64(' + idents[0] + ', ' + idents[1] + ')'; } - return idents[0] + ' ^ ' + idents[1]; + return idents[0] + '^' + idents[1]; } case 'shl': { if (bits > 32) return idents[0] + '*' + getFastValue(2, 'pow', idents[1]); - return idents[0] + ' << ' + idents[1]; + return idents[0] + '<<' + idents[1]; } case 'ashr': { 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]; + 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(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]; + 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 makeFloat(getFastValue(idents[0], '+', idents[1], item.type), item.type); @@ -2323,10 +2376,10 @@ function processMathop(item) { // 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 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]; + 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) @@ -2342,14 +2395,14 @@ 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 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 '!' + makeIsNaN(idents[0]) + ' & !' + makeIsNaN(idents[1]); - case 'uno': return makeIsNaN(idents[0]) + ' | ' + makeIsNaN(idents[1]); + 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 '!' + makeIsNaN(idents[0]) + '&!' + makeIsNaN(idents[1]); + case 'uno': return makeIsNaN(idents[0]) + '|' + makeIsNaN(idents[1]); case 'true': return '1'; default: throw 'Unknown fcmp variant: ' + variant; } @@ -2365,7 +2418,7 @@ function processMathop(item) { } case 'fpext': case 'sext': return idents[0]; case 'fptrunc': return idents[0]; - case 'select': return idents[0] + ' ? ' + asmEnsureFloat(idents[1], item.type) + ' : ' + asmEnsureFloat(idents[2], item.type); + case 'select': return idents[0] + '?' + asmEnsureFloat(idents[1], item.type) + ':' + asmEnsureFloat(idents[2], item.type); case 'ptrtoint': case 'inttoptr': { var ret = ''; if (QUANTUM_SIZE == 1) { @@ -2383,7 +2436,7 @@ 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 '((' + idents[0] + ') & ' + (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 @@ -2519,3 +2572,34 @@ function makePrintChars(s, sep) { return ret; } +function parseAlign(text) { // parse ", align \d+" + if (!text) return QUANTUM_SIZE; + return parseInt(text.substr(8)); +} + +function deParen(text) { + if (text[0] === '(') return text.substr(1, text.length-2); + return text; +} + +function addVariable(ident, type, funcData) { + funcData = funcData || Framework.currItem.funcData; + assert(type); + var old = funcData.variables[ident]; + if (old) { + assert(old.type === type); + } else { + funcData.variables[ident] = { + ident: ident, + type: type, + origin: 'added', + lineNum: 0, + rawLinesIndex: 0, + hasValueTaken: false, + pointingLevels: 0, + uses: 0, + impl: VAR_EMULATED + }; + } +} + diff --git a/src/preamble.js b/src/preamble.js index acff665f..88aaff77 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -299,11 +299,10 @@ function ccallFunc(func, returnType, argTypes, args) { function toC(value, type) { if (type == 'string') { if (value === null || value === undefined || value === 0) return 0; // null string - if (!stack) stack = Runtime.stackSave(); - var ret = Runtime.stackAlloc(value.length+1); - writeStringToMemory(value, ret); - return ret; - } else if (type == 'array') { + value = intArrayFromString(value); + type = 'array'; + } + if (type == 'array') { if (!stack) stack = Runtime.stackSave(); var ret = Runtime.stackAlloc(value.length); writeArrayToMemory(value, ret); @@ -645,7 +644,7 @@ Module['stringToUTF32'] = stringToUTF32; var PAGE_SIZE = 4096; function alignMemoryPage(x) { - return ((x+4095)>>12)<<12; + return (x+4095)&-4096; } var HEAP; @@ -717,7 +716,7 @@ var FAST_MEMORY = Module['FAST_MEMORY'] || {{{ FAST_MEMORY }}}; // Initialize the runtime's memory #if USE_TYPED_ARRAYS // check for full engine support (use string 'subarray' to avoid closure compiler confusion) -assert(!!Int32Array && !!Float64Array && !!(new Int32Array(1)['subarray']) && !!(new Int32Array(1)['set']), +assert(typeof Int32Array !== 'undefined' && typeof Float64Array !== 'undefined' && !!(new Int32Array(1)['subarray']) && !!(new Int32Array(1)['set']), 'Cannot fallback to non-typed array case: Code is too specialized'); #if USE_TYPED_ARRAYS == 1 @@ -944,6 +943,24 @@ if (!Math['toFloat32']) Math['toFloat32'] = function(x) { Math.toFloat32 = Math['toFloat32']; #endif +var Math_abs = Math.abs; +var Math_cos = Math.cos; +var Math_sin = Math.sin; +var Math_tan = Math.tan; +var Math_acos = Math.acos; +var Math_asin = Math.asin; +var Math_atan = Math.atan; +var Math_atan2 = Math.atan2; +var Math_exp = Math.exp; +var Math_log = Math.log; +var Math_sqrt = Math.sqrt; +var Math_ceil = Math.ceil; +var Math_floor = Math.floor; +var Math_pow = Math.pow; +var Math_imul = Math.imul; +var Math_toFloat32 = Math.toFloat32; +var Math_min = Math.min; + // A counter of dependencies for calling run(). If we need to // do asynchronous work before running, increment this and // decrement it. Incrementing must happen in a place like diff --git a/src/runtime.js b/src/runtime.js index 00031fed..e36068c8 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -32,8 +32,8 @@ var RuntimeGenerator = { stackEnter: function(initial, force) { if (initial === 0 && SKIP_STACK_IN_SMALL && !force) return ''; - var ret = 'var sp = ' + (ASM_JS ? '0; sp = ' : '') + 'STACKTOP'; - if (initial > 0) ret += '; STACKTOP = (STACKTOP + ' + initial + ')|0'; + var ret = 'var sp=' + (ASM_JS ? '0;sp=' : '') + 'STACKTOP'; + if (initial > 0) ret += ';STACKTOP=(STACKTOP+' + initial + ')|0'; if (USE_TYPED_ARRAYS == 2) { assert(initial % Runtime.STACK_ALIGN == 0); if (ASSERTIONS && Runtime.STACK_ALIGN == 4) { @@ -43,9 +43,6 @@ var RuntimeGenerator = { if (ASSERTIONS) { ret += '; (assert(' + asmCoercion('(STACKTOP|0) < (STACK_MAX|0)', 'i32') + ')|0)'; } - if (false) { - ret += '; _memset(' + asmCoercion('sp', 'i32') + ', 0, ' + initial + ')'; - } return ret; }, @@ -55,7 +52,7 @@ var RuntimeGenerator = { if (SAFE_HEAP) { ret += 'var i = sp; while ((i|0) < (STACKTOP|0)) { SAFE_HEAP_CLEAR(i|0); i = (i+1)|0 }'; } - return ret += 'STACKTOP = sp'; + return ret += 'STACKTOP=sp'; }, // An allocation that cannot normally be free'd (except through sbrk, which once @@ -112,8 +109,7 @@ var Runtime = { if (isNumber(target) && isNumber(quantum)) { return Math.ceil(target/quantum)*quantum; } else if (isNumber(quantum) && isPowerOfTwo(quantum)) { - var logg = log2(quantum); - return '((((' +target + ')+' + (quantum-1) + ')>>' + logg + ')<<' + logg + ')'; + return '(((' +target + ')+' + (quantum-1) + ')&' + -quantum + ')'; } return 'Math.ceil((' + target + ')/' + quantum + ')*' + quantum; }, @@ -347,22 +343,23 @@ var Runtime = { for (var i = 0; i < Runtime.functionPointers.length; i++) { if (!Runtime.functionPointers[i]) { Runtime.functionPointers[i] = func; - return 2 + 2*i; + return {{{ FUNCTION_POINTER_ALIGNMENT }}}*(1 + i); } } throw 'Finished up all reserved function pointers. Use a higher value for RESERVED_FUNCTION_POINTERS.'; #else var table = FUNCTION_TABLE; var ret = table.length; + assert(ret % {{{ FUNCTION_POINTER_ALIGNMENT }}} === 0); table.push(func); - table.push(0); + for (var i = 0; i < {{{ FUNCTION_POINTER_ALIGNMENT }}}-1; i++) table.push(0); return ret; #endif }, removeFunction: function(index) { #if ASM_JS - Runtime.functionPointers[(index-2)/2] = null; + Runtime.functionPointers[(index-{{{ FUNCTION_POINTER_ALIGNMENT }}})/{{{ FUNCTION_POINTER_ALIGNMENT }}}] = null; #else var table = FUNCTION_TABLE; table[index] = null; diff --git a/src/settings.js b/src/settings.js index 15bca4db..d2b47dc8 100644 --- a/src/settings.js +++ b/src/settings.js @@ -170,6 +170,12 @@ var ALIASING_FUNCTION_POINTERS = 0; // Whether to allow function pointers to ali // a different type. This can greatly decrease table sizes // in asm.js, but can break code that compares function // pointers across different types. +var FUNCTION_POINTER_ALIGNMENT = 2; // Byte alignment of function pointers - we will fill the + // tables with zeros on aligned values. 1 means all values + // are aligned and all will be used (which is optimal). + // Sadly 1 breaks on &Class::method function pointer calls, + // which llvm assumes have the lower bit zero (see + // test_polymorph and issue #1692). var ASM_HEAP_LOG = 0; // Simple heap logging, like SAFE_HEAP_LOG but cheaper, and in asm.js @@ -202,6 +208,7 @@ var SOCKET_WEBRTC = 0; // Select socket backend, either webrtc or websockets. var OPENAL_DEBUG = 0; // Print out debugging information from our OpenAL implementation. +var GL_ASSERTIONS = 0; // Adds extra checks for error situations in the GL library. Can impact performance. var GL_DEBUG = 0; // Print out all calls into WebGL. As with LIBRARY_DEBUG, you can set a runtime // option, in this case GL.debug. var GL_TESTING = 0; // When enabled, sets preserveDrawingBuffer in the context, to allow tests to work (but adds overhead) @@ -265,6 +272,8 @@ var CORRECT_ROUNDINGS = 1; // C rounds to 0 (-5.5 to -5, +5.5 to 5), while JS ha var FS_LOG = 0; // Log all FS operations. This is especially helpful when you're porting // a new project and want to see a list of file system operations happening // so that you can create a virtual file system with all of the required files. +var CASE_INSENSITIVE_FS = 0; // If set to nonzero, the provided virtual filesystem if treated case-insensitive, like + // Windows and OSX do. If set to 0, the VFS is case-sensitive, like on Linux. var USE_BSS = 1; // https://en.wikipedia.org/wiki/.bss // When enabled, 0-initialized globals are sorted to the end of the globals list, @@ -418,13 +427,16 @@ var EXPLICIT_ZEXT = 0; // If 1, generate an explicit conversion of zext i1 to i3 var NECESSARY_BLOCKADDRS = []; // List of (function, block) for all block addresses that are taken. -var EMIT_GENERATED_FUNCTIONS = 0; // whether to emit the list of generated functions, needed for external JS optimization passes - var JS_CHUNK_SIZE = 10240; // Used as a maximum size before breaking up expressions and lines into smaller pieces var EXPORT_NAME = 'Module'; // Global variable to export the module as for environments without a standardized module // loading system (e.g. the browser and SM shell). +var RUNNING_JS_OPTS = 0; // whether js opts will be run, after the main compiler + +var COMPILER_ASSERTIONS = 0; // costly (slow) compile-time assertions +var COMPILER_FASTPATHS = 1; // use fast-paths to speed up compilation + // Compiler debugging options var DEBUG_TAGS_SHOWING = []; // Some useful items: @@ -438,427 +450,7 @@ var DEBUG_TAGS_SHOWING = []; // metadata // legalizer -// A cached set of defines, generated from the header files. This -// lets the emscripten libc (library.js) see the right values. -// If you modify the headers or use different ones, you will need -// to override this. -var C_DEFINES = { - 'ABDAY_1': '131072', - 'ABDAY_2': '131073', - 'ABDAY_3': '131074', - 'ABDAY_4': '131075', - 'ABDAY_5': '131076', - 'ABDAY_6': '131077', - 'ABDAY_7': '131078', - 'ABMON_1': '131086', - 'ABMON_10': '131095', - 'ABMON_11': '131096', - 'ABMON_12': '131097', - 'ABMON_2': '131087', - 'ABMON_3': '131088', - 'ABMON_4': '131089', - 'ABMON_5': '131090', - 'ABMON_6': '131091', - 'ABMON_7': '131092', - 'ABMON_8': '131093', - 'ABMON_9': '131094', - 'AF_INET': '2', - 'AF_INET6': '10', - 'AF_UNSPEC': '0', - 'AI_ADDRCONFIG': '32', - 'AI_ALL': '16', - 'AI_CANONNAME': '2', - 'AI_NUMERICHOST': '4', - 'AI_NUMERICSERV': '1024', - 'AI_PASSIVE': '1', - 'AI_V4MAPPED': '8', - 'ALT_DIGITS': '131119', - 'AM_STR': '131110', - 'CLOCKS_PER_SEC': '1000000', - 'CODESET': '14', - 'CRNCYSTR': '262159', - 'DAY_1': '131079', - 'DAY_2': '131080', - 'DAY_3': '131081', - 'DAY_4': '131082', - 'DAY_5': '131083', - 'DAY_6': '131084', - 'DAY_7': '131085', - 'D_FMT': '131113', - 'D_T_FMT': '131112', - 'E2BIG': '7', - 'EACCES': '13', - 'EADDRINUSE': '98', - 'EADDRNOTAVAIL': '99', - 'EADV': '68', - 'EAFNOSUPPORT': '97', - 'EAGAIN': '11', - 'EAI_BADFLAGS': '-1', - 'EAI_FAMILY': '-6', - 'EAI_NONAME': '-2', - 'EAI_OVERFLOW': '-12', - 'EAI_SERVICE': '-8', - 'EAI_SOCKTYPE': '-7', - 'EALREADY': '114', - 'EBADE': '52', - 'EBADF': '9', - 'EBADFD': '77', - 'EBADMSG': '74', - 'EBADR': '53', - 'EBADRQC': '56', - 'EBADSLT': '57', - 'EBFONT': '59', - 'EBUSY': '16', - 'ECANCELED': '125', - 'ECHILD': '10', - 'ECHRNG': '44', - 'ECOMM': '70', - 'ECONNABORTED': '103', - 'ECONNREFUSED': '111', - 'ECONNRESET': '104', - 'EDEADLK': '35', - 'EDEADLOCK': '35', - 'EDESTADDRREQ': '89', - 'EDOM': '33', - 'EDOTDOT': '73', - 'EDQUOT': '122', - 'EEXIST': '17', - 'EFAULT': '14', - 'EFBIG': '27', - 'EHOSTDOWN': '112', - 'EHOSTUNREACH': '113', - 'EIDRM': '43', - 'EILSEQ': '84', - 'EINPROGRESS': '115', - 'EINTR': '4', - 'EINVAL': '22', - 'EIO': '5', - 'EISCONN': '106', - 'EISDIR': '21', - 'EL2HLT': '51', - 'EL2NSYNC': '45', - 'EL3HLT': '46', - 'EL3RST': '47', - 'ELIBACC': '79', - 'ELIBBAD': '80', - 'ELIBEXEC': '83', - 'ELIBMAX': '82', - 'ELIBSCN': '81', - 'ELNRNG': '48', - 'ELOOP': '40', - 'EMFILE': '24', - 'EMLINK': '31', - 'EMSGSIZE': '90', - 'EMULTIHOP': '72', - 'ENAMETOOLONG': '36', - 'ENETDOWN': '100', - 'ENETRESET': '102', - 'ENETUNREACH': '101', - 'ENFILE': '23', - 'ENOANO': '55', - 'ENOBUFS': '105', - 'ENOCSI': '50', - 'ENODATA': '61', - 'ENODEV': '19', - 'ENOENT': '2', - 'ENOEXEC': '8', - 'ENOLCK': '37', - 'ENOLINK': '67', - 'ENOMEDIUM': '123', - 'ENOMEM': '12', - 'ENOMSG': '42', - 'ENONET': '64', - 'ENOPKG': '65', - 'ENOPROTOOPT': '92', - 'ENOSPC': '28', - 'ENOSR': '63', - 'ENOSTR': '60', - 'ENOSYS': '38', - 'ENOTBLK': '15', - 'ENOTCONN': '107', - 'ENOTDIR': '20', - 'ENOTEMPTY': '39', - 'ENOTRECOVERABLE': '131', - 'ENOTSOCK': '88', - 'ENOTSUP': '95', - 'ENOTTY': '25', - 'ENOTUNIQ': '76', - 'ENXIO': '6', - 'EOF': '-1', - 'EOPNOTSUPP': '95', - 'EOVERFLOW': '75', - 'EOWNERDEAD': '130', - 'EPERM': '1', - 'EPFNOSUPPORT': '96', - 'EPIPE': '32', - 'EPROTO': '71', - 'EPROTONOSUPPORT': '93', - 'EPROTOTYPE': '91', - 'ERA': '131116', - 'ERANGE': '34', - 'ERA_D_FMT': '131118', - 'ERA_D_T_FMT': '131120', - 'ERA_T_FMT': '131121', - 'EREMCHG': '78', - 'EREMOTE': '66', - 'EROFS': '30', - 'ESHUTDOWN': '108', - 'ESOCKTNOSUPPORT': '94', - 'ESPIPE': '29', - 'ESRCH': '3', - 'ESRMNT': '69', - 'ESTALE': '116', - 'ESTRPIPE': '86', - 'ETIME': '62', - 'ETIMEDOUT': '110', - 'ETOOMANYREFS': '109', - 'ETXTBSY': '26', - 'EUNATCH': '49', - 'EUSERS': '87', - 'EWOULDBLOCK': '11', - 'EXDEV': '18', - 'EXFULL': '54', - 'FIONREAD': '21531', - 'FP_INFINITE': '1', - 'FP_NAN': '0', - 'FP_NORMAL': '4', - 'FP_ZERO': '2', - 'F_DUPFD': '0', - 'F_GETFD': '1', - 'F_GETFL': '3', - 'F_GETLK': '12', - 'F_GETLK64': '12', - 'F_GETOWN': '9', - 'F_SETFD': '2', - 'F_SETFL': '4', - 'F_SETLK': '13', - 'F_SETLK64': '13', - 'F_SETLKW': '14', - 'F_SETLKW64': '14', - 'F_SETOWN': '8', - 'F_UNLCK': '2', - 'INADDR_LOOPBACK': '2130706433', - 'IPPROTO_TCP': '6', - 'IPPROTO_UDP': '17', - 'MAP_PRIVATE': '2', - 'MON_1': '131098', - 'MON_10': '131107', - 'MON_11': '131108', - 'MON_12': '131109', - 'MON_2': '131099', - 'MON_3': '131100', - 'MON_4': '131101', - 'MON_5': '131102', - 'MON_6': '131103', - 'MON_7': '131104', - 'MON_8': '131105', - 'MON_9': '131106', - 'NI_NAMEREQD': '8', - 'NI_NUMERICHOST': '1', - 'NOEXPR': '327681', - 'O_ACCMODE': '2097155', - 'O_APPEND': '1024', - 'O_CREAT': '64', - 'O_EXCL': '128', - 'O_NOFOLLOW': '131072', - 'O_RDONLY': '0', - 'O_RDWR': '2', - 'O_SYNC': '1052672', - 'O_TRUNC': '512', - 'O_WRONLY': '1', - 'PM_STR': '131111', - 'POLLERR': '8', - 'POLLHUP': '16', - 'POLLIN': '1', - 'POLLNVAL': '32', - 'POLLOUT': '4', - 'POLLPRI': '2', - 'POLLRDNORM': '64', - 'RADIXCHAR': '65536', - 'R_OK': '4', - 'SEEK_END': '2', - 'SEEK_SET': '0', - 'SOCK_DGRAM': '2', - 'SOCK_STREAM': '1', - 'S_IALLUGO': '4095', - 'S_IFBLK': '24576', - 'S_IFCHR': '8192', - 'S_IFDIR': '16384', - 'S_IFIFO': '4096', - 'S_IFLNK': '40960', - 'S_IFMT': '61440', - 'S_IFREG': '32768', - 'S_IFSOCK': '49152', - 'S_IRUGO': '292', - 'S_IRWXO': '7', - 'S_IRWXUGO': '511', - 'S_ISVTX': '512', - 'S_IWUGO': '146', - 'S_IXUGO': '73', - 'THOUSEP': '65537', - 'T_FMT': '131114', - 'T_FMT_AMPM': '131115', - 'W_OK': '2', - 'X_OK': '1', - 'YESEXPR': '327680', - '_CS_GNU_LIBC_VERSION': '2', - '_CS_GNU_LIBPTHREAD_VERSION': '3', - '_CS_PATH': '0', - '_CS_POSIX_V6_ILP32_OFF32_CFLAGS': '1116', - '_CS_POSIX_V6_ILP32_OFF32_LDFLAGS': '1117', - '_CS_POSIX_V6_ILP32_OFF32_LIBS': '1118', - '_CS_POSIX_V6_ILP32_OFFBIG_CFLAGS': '1120', - '_CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS': '1121', - '_CS_POSIX_V6_ILP32_OFFBIG_LIBS': '1122', - '_CS_POSIX_V6_LP64_OFF64_CFLAGS': '1124', - '_CS_POSIX_V6_LP64_OFF64_LDFLAGS': '1125', - '_CS_POSIX_V6_LP64_OFF64_LIBS': '1126', - '_CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS': '1128', - '_CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS': '1129', - '_CS_POSIX_V6_LPBIG_OFFBIG_LIBS': '1130', - '_CS_POSIX_V6_WIDTH_RESTRICTED_ENVS': '1', - '_PC_2_SYMLINKS': '20', - '_PC_ALLOC_SIZE_MIN': '18', - '_PC_ASYNC_IO': '10', - '_PC_CHOWN_RESTRICTED': '6', - '_PC_FILESIZEBITS': '13', - '_PC_LINK_MAX': '0', - '_PC_MAX_CANON': '1', - '_PC_MAX_INPUT': '2', - '_PC_NAME_MAX': '3', - '_PC_NO_TRUNC': '7', - '_PC_PATH_MAX': '4', - '_PC_PIPE_BUF': '5', - '_PC_PRIO_IO': '11', - '_PC_REC_INCR_XFER_SIZE': '14', - '_PC_REC_MAX_XFER_SIZE': '15', - '_PC_REC_MIN_XFER_SIZE': '16', - '_PC_REC_XFER_ALIGN': '17', - '_PC_SOCK_MAXBUF': '12', - '_PC_SYMLINK_MAX': '19', - '_PC_SYNC_IO': '9', - '_PC_VDISABLE': '8', - '_SC_2_CHAR_TERM': '95', - '_SC_2_C_BIND': '47', - '_SC_2_C_DEV': '48', - '_SC_2_FORT_DEV': '49', - '_SC_2_FORT_RUN': '50', - '_SC_2_LOCALEDEF': '52', - '_SC_2_PBS': '168', - '_SC_2_PBS_ACCOUNTING': '169', - '_SC_2_PBS_CHECKPOINT': '175', - '_SC_2_PBS_LOCATE': '170', - '_SC_2_PBS_MESSAGE': '171', - '_SC_2_PBS_TRACK': '172', - '_SC_2_SW_DEV': '51', - '_SC_2_UPE': '97', - '_SC_2_VERSION': '46', - '_SC_ADVISORY_INFO': '132', - '_SC_AIO_LISTIO_MAX': '23', - '_SC_AIO_MAX': '24', - '_SC_AIO_PRIO_DELTA_MAX': '25', - '_SC_ARG_MAX': '0', - '_SC_ASYNCHRONOUS_IO': '12', - '_SC_ATEXIT_MAX': '87', - '_SC_BARRIERS': '133', - '_SC_BC_BASE_MAX': '36', - '_SC_BC_DIM_MAX': '37', - '_SC_BC_SCALE_MAX': '38', - '_SC_BC_STRING_MAX': '39', - '_SC_CHILD_MAX': '1', - '_SC_CLK_TCK': '2', - '_SC_CLOCK_SELECTION': '137', - '_SC_COLL_WEIGHTS_MAX': '40', - '_SC_CPUTIME': '138', - '_SC_DELAYTIMER_MAX': '26', - '_SC_EXPR_NEST_MAX': '42', - '_SC_FSYNC': '15', - '_SC_GETGR_R_SIZE_MAX': '69', - '_SC_GETPW_R_SIZE_MAX': '70', - '_SC_HOST_NAME_MAX': '180', - '_SC_IOV_MAX': '60', - '_SC_IPV6': '235', - '_SC_JOB_CONTROL': '7', - '_SC_LINE_MAX': '43', - '_SC_LOGIN_NAME_MAX': '71', - '_SC_MAPPED_FILES': '16', - '_SC_MEMLOCK': '17', - '_SC_MEMLOCK_RANGE': '18', - '_SC_MEMORY_PROTECTION': '19', - '_SC_MESSAGE_PASSING': '20', - '_SC_MONOTONIC_CLOCK': '149', - '_SC_MQ_OPEN_MAX': '27', - '_SC_MQ_PRIO_MAX': '28', - '_SC_NGROUPS_MAX': '3', - '_SC_NPROCESSORS_ONLN': '84', - '_SC_OPEN_MAX': '4', - '_SC_PAGE_SIZE': '30', - '_SC_PRIORITIZED_IO': '13', - '_SC_PRIORITY_SCHEDULING': '10', - '_SC_RAW_SOCKETS': '236', - '_SC_READER_WRITER_LOCKS': '153', - '_SC_REALTIME_SIGNALS': '9', - '_SC_REGEXP': '155', - '_SC_RE_DUP_MAX': '44', - '_SC_RTSIG_MAX': '31', - '_SC_SAVED_IDS': '8', - '_SC_SEMAPHORES': '21', - '_SC_SEM_NSEMS_MAX': '32', - '_SC_SEM_VALUE_MAX': '33', - '_SC_SHARED_MEMORY_OBJECTS': '22', - '_SC_SHELL': '157', - '_SC_SIGQUEUE_MAX': '34', - '_SC_SPAWN': '159', - '_SC_SPIN_LOCKS': '154', - '_SC_SPORADIC_SERVER': '160', - '_SC_STREAM_MAX': '5', - '_SC_SYMLOOP_MAX': '173', - '_SC_SYNCHRONIZED_IO': '14', - '_SC_THREADS': '67', - '_SC_THREAD_ATTR_STACKADDR': '77', - '_SC_THREAD_ATTR_STACKSIZE': '78', - '_SC_THREAD_CPUTIME': '139', - '_SC_THREAD_DESTRUCTOR_ITERATIONS': '73', - '_SC_THREAD_KEYS_MAX': '74', - '_SC_THREAD_PRIORITY_SCHEDULING': '79', - '_SC_THREAD_PRIO_INHERIT': '80', - '_SC_THREAD_PRIO_PROTECT': '81', - '_SC_THREAD_PROCESS_SHARED': '82', - '_SC_THREAD_SAFE_FUNCTIONS': '68', - '_SC_THREAD_SPORADIC_SERVER': '161', - '_SC_THREAD_STACK_MIN': '75', - '_SC_THREAD_THREADS_MAX': '76', - '_SC_TIMEOUTS': '164', - '_SC_TIMERS': '11', - '_SC_TIMER_MAX': '35', - '_SC_TRACE': '181', - '_SC_TRACE_EVENT_FILTER': '182', - '_SC_TRACE_EVENT_NAME_MAX': '242', - '_SC_TRACE_INHERIT': '183', - '_SC_TRACE_LOG': '184', - '_SC_TRACE_NAME_MAX': '243', - '_SC_TRACE_SYS_MAX': '244', - '_SC_TRACE_USER_EVENT_MAX': '245', - '_SC_TTY_NAME_MAX': '72', - '_SC_TYPED_MEMORY_OBJECTS': '165', - '_SC_TZNAME_MAX': '6', - '_SC_V6_ILP32_OFF32': '176', - '_SC_V6_ILP32_OFFBIG': '177', - '_SC_V6_LP64_OFF64': '178', - '_SC_V6_LPBIG_OFFBIG': '179', - '_SC_VERSION': '29', - '_SC_XBS5_ILP32_OFF32': '125', - '_SC_XBS5_ILP32_OFFBIG': '126', - '_SC_XBS5_LP64_OFF64': '127', - '_SC_XBS5_LPBIG_OFFBIG': '128', - '_SC_XOPEN_CRYPT': '92', - '_SC_XOPEN_ENH_I18N': '93', - '_SC_XOPEN_LEGACY': '129', - '_SC_XOPEN_REALTIME': '130', - '_SC_XOPEN_REALTIME_THREADS': '131', - '_SC_XOPEN_SHM': '94', - '_SC_XOPEN_STREAMS': '246', - '_SC_XOPEN_UNIX': '91', - '_SC_XOPEN_VERSION': '89' -}; - +// The list of defines (C_DEFINES) was moved into struct_info.json in the same directory. +// That file is automatically parsed by tools/gen_struct_info.py. +// If you modify the headers, just clear your cache and emscripten libc should see +// the new values. diff --git a/src/struct_info.json b/src/struct_info.json new file mode 100644 index 00000000..5b4726e8 --- /dev/null +++ b/src/struct_info.json @@ -0,0 +1,1045 @@ +[ + // =========================================== + // libc + // =========================================== + { + "file": "libc/dirent.h", + "defines": [], + "structs": { + "dirent": [ + "d_ino", + "d_off", + "d_reclen", + "d_type", + "d_name" + ] + } + }, + { + "file": "libc/utime.h", + "defines": [], + "structs": { + "utimbuf": [ + "actime", + "modtime" + ] + } + }, + { + "file": "libc/sys/stat.h", + "defines": [ + "S_IFDIR", + "S_IFREG", + "S_IFMT", + "S_IFIFO", + "S_IFSOCK", + "S_IFBLK", + "S_IFLNK", + "S_IFCHR" + ], + "structs": { + "stat": [ + "st_dev", + "__st_dev_padding", + "__st_ino_truncated", + "st_mode", + "st_nlink", + "st_uid", + "st_gid", + "st_rdev", + "__st_rdev_padding", + "st_size", + "st_blksize", + "st_blocks", + { + "st_atim": [ + "tv_sec", + "tv_nsec" + ] + }, + { + "st_mtim": [ + "tv_sec", + "tv_nsec" + ] + }, + { + "st_ctim": [ + "tv_sec", + "tv_nsec" + ] + }, + "st_ino" + ] + } + }, + { + "file": "libc/sys/statvfs.h", + "defines": [], + "structs": { + "statvfs": [ + "f_bsize", + "f_frsize", + "f_blocks", + "f_bfree", + "f_bavail", + "f_files", + "f_ffree", + "f_favail", + "f_fsid", + "f_flag", + "f_namemax" + ] + } + }, + { + "file": "libc/fcntl.h", + "defines": [ + "F_UNLCK", + "O_RDWR", + "S_IRWXO", + "F_GETLK64", + "F_SETLKW64", + "F_SETLKW", + "F_SETLK64", + "F_GETLK", + "S_ISVTX", + "O_RDONLY", + "O_ACCMODE", + "F_DUPFD", + "F_SETLK", + "O_WRONLY" + ], + "structs": { + "flock": [ + "l_type", + "l_whence", + "l_start", + "l_len", + "l_pid" + ] + } + }, + { + "file": "libc/poll.h", + "defines": [ + "POLLHUP", + "POLLERR", + "POLLRDNORM", + "POLLPRI", + "POLLIN", + "POLLOUT", + "POLLNVAL" + ], + "structs": { + "pollfd": [ + "fd", + "events", + "revents" + ] + } + }, + { + "file": "libc/stdlib.h", + "defines": [], + "structs": { + // NOTE: The hash sign at the end of this name is a hint to the processor that it mustn't prefix "struct " to the name to reference this struct. + // It will be stripped away when writing the compiled JSON file. You can just refer to it as C_STRUCTS.div_t when using it in the JS code. + // For more information see gen_inspect_code() in tools/gen_struct_info.py . + "div_t#": [ + "quot", + "rem" + ] + } + }, + { + "file": "libc/sys/utsname.h", + "defines": [], + "structs": { + "utsname": [ + "sysname", + "nodename", + "release", + "version", + "machine", + "domainname" + ] + } + }, + { + "file": "libc/time.h", + "defines": [ + ["li", "CLOCKS_PER_SEC"] + ], + "structs": { + "timezone": [ + "tz_minuteswest", + "tz_dsttime" + ], + "tm": [ + "tm_sec", + "tm_min", + "tm_hour", + "tm_mday", + "tm_mon", + "tm_year", + "tm_wday", + "tm_yday", + "tm_isdst", + "tm_gmtoff", + "tm_zone" + ], + "itimerspec": [ + { + "it_interval": [ + "tv_sec", + "tv_nsec" + ] + }, + { + "it_value": [ + "tv_sec", + "tv_nsec" + ] + } + ], + "timespec": [ + "tv_sec", + "tv_nsec" + ], + "timeval": [ + "tv_sec", + "tv_usec" + ] + } + }, + { + "file": "libc/sys/times.h", + "defines": [], + "structs": { + "tms": [ + "tms_utime", + "tms_stime", + "tms_cutime", + "tms_cstime" + ] + } + }, + { + "defines": [], + "file": "compat/sys/timeb.h", + "structs": { + "timeb": [ + "time", + "millitm", + "timezone", + "dstflag" + ] + } + }, + { + "file": "libc/sys/resource.h", + "defines": [], + "structs": { + "rlimit": [ + "rlim_cur", + "rlim_max" + ], + "rusage": [ + { + "ru_utime": [ + "tv_sec", + "tv_usec" + ] + }, + { + "ru_stime": [ + "tv_sec", + "tv_usec" + ] + }, + "ru_maxrss", + "ru_ixrss", + "ru_idrss", + "ru_isrss", + "ru_minflt", + "ru_majflt", + "ru_nswap", + "ru_inblock", + "ru_oublock", + "ru_msgsnd", + "ru_msgrcv", + "ru_nsignals", + "ru_nvcsw", + "ru_nivcsw" + ] + } + }, + { + "file": "libc/netdb.h", + "defines": [ + "AI_V4MAPPED", + "EAI_SERVICE", + "EAI_FAMILY", + "AI_ALL", + "AI_ADDRCONFIG", + "AI_NUMERICSERV", + "NI_NUMERICHOST", + "EAI_OVERFLOW", + "AI_NUMERICHOST", + "AI_CANONNAME", + "AI_PASSIVE", + "NI_NAMEREQD", + "EAI_NONAME", + "EAI_SOCKTYPE", + "EAI_BADFLAGS" + ], + "structs": { + "sockaddr": [ + "sa_family", + "sa_data" + ], + "hostent": [ + "h_name", + "h_aliases", + "h_addrtype", + "h_length", + "h_addr_list" + ], + "addrinfo": [ + "ai_flags", + "ai_family", + "ai_socktype", + "ai_protocol", + "ai_addrlen", + "ai_addr", + "ai_canonname", + "ai_next" + ], + "in_addr": [ + "s_addr" + ], + "linger": [ + "l_onoff", + "l_linger" + ], + "protoent": [ + "p_name", + "p_aliases", + "p_proto" + ], + "sockaddr_in": [ + "sin_family", + "sin_port", + { + "sin_addr": [ + "s_addr" + ] + }, + "sin_zero" + ], + "iovec": [ + "iov_base", + "iov_len" + ], + "cmsghdr": [ + "cmsg_len", + "cmsg_level", + "cmsg_type" + ], + "sockaddr_in6": [ + "sin6_family", + "sin6_port", + "sin6_flowinfo", + { + "sin6_addr": [ + { + "__in6_union": [ + "__s6_addr", + "__s6_addr16", + "__s6_addr32" + ] + } + ] + }, + "sin6_scope_id" + ], + "msghdr": [ + "msg_name", + "msg_namelen", + "msg_iov", + "msg_iovlen", + "msg_control", + "msg_controllen", + "msg_flags" + ], + "in6_addr": [ + { + "__in6_union": [ + "__s6_addr", + "__s6_addr16", + "__s6_addr32" + ] + } + ], + "netent": [ + "n_name", + "n_aliases", + "n_addrtype", + "n_net" + ] + } + }, + { + "file": "libc/netinet/in.h", + "defines": [ + "IPPROTO_UDP", + "IPPROTO_TCP", + "INADDR_LOOPBACK" + ], + "structs": {} + }, + { + "file": "libc/math.h", + "defines": [ + "FP_ZERO", + "FP_NAN", + "FP_INFINITE", + "FP_NORMAL" + ], + "structs": {} + }, + { + "file": "libc/bits/fcntl.h", + "defines": [ + "O_CREAT", + "O_SYNC", + "F_GETFD", + "F_SETFL", + "O_NOFOLLOW", + "O_APPEND", + "F_SETOWN", + "O_TRUNC", + "F_GETOWN", + "F_SETFD", + "O_EXCL", + "F_GETFL" + ], + "structs": {} + }, + { + "file": "libc/sys/socket.h", + "defines": [ + "SOCK_DGRAM", + "SOCK_STREAM", + "AF_INET", + "AF_UNSPEC", + "AF_INET6" + ], + "structs": {} + }, + { + "file": "libc/bits/ioctl.h", + "defines": [ + "FIONREAD" + ], + "structs": {} + }, + { + "file": "libc/unistd.h", + "defines": [ + "_SC_XOPEN_LEGACY", + "_SC_XOPEN_VERSION", + "_SC_BC_DIM_MAX", + "_CS_POSIX_V6_LP64_OFF64_LIBS", + "_PC_REC_MIN_XFER_SIZE", + "_SC_V6_ILP32_OFFBIG", + "_SC_MEMLOCK", + "_SC_THREADS", + "_PC_SOCK_MAXBUF", + "_SC_THREAD_KEYS_MAX", + "_SC_2_PBS", + "_SC_TRACE_INHERIT", + "_SC_REGEXP", + "_CS_POSIX_V6_LP64_OFF64_CFLAGS", + "_SC_VERSION", + "_PC_CHOWN_RESTRICTED", + "_SC_MQ_PRIO_MAX", + "_SC_PAGE_SIZE", + "_SC_BARRIERS", + "_SC_2_LOCALEDEF", + "_SC_STREAM_MAX", + "_SC_TIMERS", + "_PC_PATH_MAX", + "_SC_SPORADIC_SERVER", + "_SC_NPROCESSORS_ONLN", + "_CS_POSIX_V6_LPBIG_OFFBIG_LIBS", + "_PC_MAX_INPUT", + "_SC_CLK_TCK", + "_SC_AIO_MAX", + "_SC_THREAD_PRIO_INHERIT", + "_PC_2_SYMLINKS", + "_SC_SPAWN", + "_CS_POSIX_V6_ILP32_OFF32_LDFLAGS", + "_CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS", + "_SC_TRACE_SYS_MAX", + "_CS_POSIX_V6_WIDTH_RESTRICTED_ENVS", + "_SC_AIO_PRIO_DELTA_MAX", + "_SC_MONOTONIC_CLOCK", + "_SC_XOPEN_ENH_I18N", + "_SC_SPIN_LOCKS", + "_SC_XOPEN_SHM", + "_PC_LINK_MAX", + "_SC_FSYNC", + "_SC_GETGR_R_SIZE_MAX", + "_SC_TRACE_NAME_MAX", + "_SC_BC_BASE_MAX", + "_SC_XOPEN_STREAMS", + "_SC_GETPW_R_SIZE_MAX", + "_SC_CPUTIME", + "_SC_XBS5_ILP32_OFFBIG", + "_SC_TRACE_EVENT_FILTER", + "_SC_OPEN_MAX", + "_SC_2_FORT_RUN", + "_SC_RE_DUP_MAX", + "_SC_THREAD_PRIO_PROTECT", + "_SC_2_PBS_CHECKPOINT", + "_SC_XBS5_LPBIG_OFFBIG", + "_SC_SHARED_MEMORY_OBJECTS", + "_PC_ALLOC_SIZE_MIN", + "_SC_READER_WRITER_LOCKS", + "_SC_MEMLOCK_RANGE", + "_SC_PRIORITY_SCHEDULING", + "_PC_VDISABLE", + "_SC_MESSAGE_PASSING", + "_SC_THREAD_ATTR_STACKADDR", + "_SC_THREAD_THREADS_MAX", + "_SC_LOGIN_NAME_MAX", + "_SC_2_C_BIND", + "_PC_NO_TRUNC", + "_SC_SHELL", + "_SC_V6_LP64_OFF64", + "_CS_GNU_LIBC_VERSION", + "_SC_SEM_VALUE_MAX", + "_SC_MQ_OPEN_MAX", + "_SC_HOST_NAME_MAX", + "_SC_THREAD_STACK_MIN", + "_SC_TIMEOUTS", + "_SC_CHILD_MAX", + "_SC_2_PBS_MESSAGE", + "_SC_2_C_DEV", + "_SC_TIMER_MAX", + "_SC_SYMLOOP_MAX", + "_PC_REC_XFER_ALIGN", + "_SC_REALTIME_SIGNALS", + "_PC_ASYNC_IO", + "_SC_MAPPED_FILES", + "_SC_NGROUPS_MAX", + "_SC_SEMAPHORES", + "_SC_TRACE_LOG", + "_CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS", + "_CS_POSIX_V6_LP64_OFF64_LDFLAGS", + "_SC_THREAD_DESTRUCTOR_ITERATIONS", + "_SC_TRACE_EVENT_NAME_MAX", + "_SC_BC_STRING_MAX", + "_SC_2_SW_DEV", + "_SC_ARG_MAX", + "_SC_THREAD_PRIORITY_SCHEDULING", + "_SC_THREAD_CPUTIME", + "_CS_POSIX_V6_ILP32_OFF32_LIBS", + "_SC_SYNCHRONIZED_IO", + "_CS_POSIX_V6_ILP32_OFF32_CFLAGS", + "_SC_MEMORY_PROTECTION", + "_PC_PRIO_IO", + "_SC_V6_LPBIG_OFFBIG", + "_SC_EXPR_NEST_MAX", + "_CS_POSIX_V6_ILP32_OFFBIG_CFLAGS", + "_PC_REC_MAX_XFER_SIZE", + "_SC_DELAYTIMER_MAX", + "W_OK", + "R_OK", + "_SC_XOPEN_CRYPT", + "_SC_XBS5_LP64_OFF64", + "_SC_SIGQUEUE_MAX", + "_SC_TZNAME_MAX", + "_CS_PATH", + "_PC_MAX_CANON", + "_SC_THREAD_SAFE_FUNCTIONS", + "_PC_NAME_MAX", + "_SC_TRACE_USER_EVENT_MAX", + "_SC_RAW_SOCKETS", + "_SC_RTSIG_MAX", + "_SC_PRIORITIZED_IO", + "_SC_XOPEN_UNIX", + "_PC_REC_INCR_XFER_SIZE", + "_PC_FILESIZEBITS", + "_SC_XBS5_ILP32_OFF32", + "_CS_GNU_LIBPTHREAD_VERSION", + "_SC_2_PBS_LOCATE", + "_SC_V6_ILP32_OFF32", + "_PC_SYNC_IO", + "_SC_2_UPE", + "_SC_SEM_NSEMS_MAX", + "_SC_IOV_MAX", + "_SC_TRACE", + "_CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS", + "_SC_LINE_MAX", + "_CS_POSIX_V6_ILP32_OFFBIG_LIBS", + "_SC_2_FORT_DEV", + "_SC_ATEXIT_MAX", + "_SC_SAVED_IDS", + "_SC_2_PBS_TRACK", + "_SC_THREAD_PROCESS_SHARED", + "_SC_JOB_CONTROL", + "_SC_IPV6", + "_SC_ADVISORY_INFO", + "_SC_XOPEN_REALTIME_THREADS", + "_PC_SYMLINK_MAX", + "X_OK", + "_SC_COLL_WEIGHTS_MAX", + "_SC_CLOCK_SELECTION", + "_SC_XOPEN_REALTIME", + "_PC_PIPE_BUF", + "_SC_2_PBS_ACCOUNTING", + "_SC_THREAD_SPORADIC_SERVER", + "_SC_THREAD_ATTR_STACKSIZE", + "_SC_2_VERSION", + "_SC_TYPED_MEMORY_OBJECTS", + "_SC_ASYNCHRONOUS_IO", + "_SC_2_CHAR_TERM", + "_SC_AIO_LISTIO_MAX", + "_SC_BC_SCALE_MAX", + "_SC_TTY_NAME_MAX" + ], + "structs": {} + }, + { + "file": "libc/bits/errno.h", + "defines": [ + "ETXTBSY", + "ETOOMANYREFS", + "ENAMETOOLONG", + "ENOPKG", + "EL3HLT", + "EINPROGRESS", + "ENOTSOCK", + "ENOTSUP", + "EFBIG", + "ENOLINK", + "EL3RST", + "ENOTUNIQ", + "ELNRNG", + "ENOANO", + "ENOPROTOOPT", + "E2BIG", + "EHOSTDOWN", + "EBFONT", + "ENOTEMPTY", + "EBUSY", + "EADDRINUSE", + "ELIBACC", + "EDQUOT", + "ENOENT", + "ECOMM", + "EXFULL", + "ENOTDIR", + "ENETRESET", + "EAFNOSUPPORT", + "EINVAL", + "ENODEV", + "ENOCSI", + "EPROTONOSUPPORT", + "ETIME", + "ENOTTY", + "EAGAIN", + "EMSGSIZE", + "ELIBEXEC", + "EMLINK", + "ECANCELED", + "EDESTADDRREQ", + "EADDRNOTAVAIL", + "EPERM", + "EPROTOTYPE", + "ENOMEDIUM", + "ELOOP", + "EREMOTE", + "ELIBMAX", + "EMULTIHOP", + "ECONNABORTED", + "EFAULT", + "EBADMSG", + "EDOM", + "EILSEQ", + "EPFNOSUPPORT", + "ENONET", + "ECHRNG", + "ESRCH", + "EHOSTUNREACH", + "EL2HLT", + "EL2NSYNC", + "ENOMSG", + "EISDIR", + "EDEADLOCK", + "ECONNRESET", + "ESTRPIPE", + "ESHUTDOWN", + "EDEADLK", + "EBADRQC", + "EUNATCH", + "ECHILD", + "ETIMEDOUT", + "EALREADY", + "ENXIO", + "EMFILE", + "ENFILE", + "EREMCHG", + "ENOMEM", + "ENOSR", + "EOWNERDEAD", + "ELIBSCN", + "EPIPE", + "EBADSLT", + "ENOSTR", + "EIO", + "EWOULDBLOCK", + "EBADE", + "ENODATA", + "ESOCKTNOSUPPORT", + "ENOLCK", + "EPROTO", + "ESRMNT", + "EXDEV", + "ENOSPC", + "ELIBBAD", + "ERANGE", + "ESTALE", + "ENOTRECOVERABLE", + "ENOBUFS", + "EIDRM", + "EINTR", + "EADV", + "ENOSYS", + "EUSERS", + "EOPNOTSUPP", + "ENOTCONN", + "ENETUNREACH", + "ESPIPE", + "EROFS", + "ECONNREFUSED", + "ENETDOWN", + "ENOEXEC", + "EBADF", + "EDOTDOT", + "EBADFD", + "EBADR", + "EISCONN", + "ENOTBLK", + "EOVERFLOW" + ], + "structs": {} + }, + { + "file": "libc/langinfo.h", + "defines": [ + "ABDAY_7", + "ABDAY_6", + "ABDAY_5", + "ABDAY_4", + "ABDAY_3", + "ABDAY_2", + "ABDAY_1", + "ABMON_1", + "RADIXCHAR", + "ABMON_3", + "AM_STR", + "ALT_DIGITS", + "PM_STR", + "ABMON_9", + "YESEXPR", + "ABMON_2", + "ABMON_7", + "ABMON_6", + "ABMON_5", + "ABMON_4", + "ABMON_8", + "ERA", + "MON_2", + "MON_3", + "MON_1", + "MON_6", + "MON_7", + "MON_4", + "MON_5", + "MON_8", + "MON_9", + "NOEXPR", + "T_FMT_AMPM", + "MON_10", + "MON_11", + "MON_12", + "T_FMT", + "THOUSEP", + "ERA_T_FMT", + "ERA_D_T_FMT", + "D_FMT", + "DAY_2", + "DAY_3", + "DAY_1", + "DAY_6", + "DAY_7", + "DAY_4", + "DAY_5", + "ERA_D_FMT", + "CODESET", + "D_T_FMT", + "CRNCYSTR", + "ABMON_12", + "ABMON_11", + "ABMON_10" + ], + "structs": {} + }, + { + "file": "libc/stdio.h", + "defines": [ + "EOF", + "SEEK_END", + "SEEK_SET" + ], + "structs": {} + }, + { + "file": "libc/arpa/tftp.h", + "defines": [ + "EACCES", + "EEXIST" + ], + "structs": {} + }, + { + "file": "compat/sys/stat.h", + "defines": [ + "S_IALLUGO", + "S_IWUGO", + "S_IRUGO", + "S_IRWXUGO", + "S_IXUGO" + ], + "structs": {} + }, + { + "file": "libc/bits/mman.h", + "defines": [ + "MAP_PRIVATE" + ], + "structs": {} + }, + + // =========================================== + // SDL + // =========================================== + { + "file": "SDL/SDL_rect.h", + "defines": [], + "structs": { + "SDL_Rect": [ + "x", + "y", + "w", + "h" + ] + } + }, + { + "file": "SDL/SDL_keyboard.h", + "defines": [], + "structs": { + "SDL_Keysym": [ + "scancode", + "sym", + "mod", + "unicode" + ] + } + }, + { + "file": "SDL/SDL_pixels.h", + "defines": [], + "structs": { + "SDL_Palette": [ + "ncolors", + "colors", + "version", + "refcount" + ], + "SDL_PixelFormat": [ + "format", + "palette", + "BitsPerPixel", + "BytesPerPixel", + "padding", + "Rmask", + "Gmask", + "Bmask", + "Amask", + "Rloss", + "Gloss", + "Bloss", + "Aloss", + "Rshift", + "Gshift", + "Bshift", + "Ashift", + "refcount", + "next" + ], + "SDL_Color": [ + "r", + "g", + "b", + "unused" + ] + } + }, + { + "file": "SDL/SDL_surface.h", + "defines": [], + "structs": { + "SDL_Surface": [ + "flags", + "format", + "w", + "h", + "pitch", + "pixels", + "userdata", + "locked", + "lock_data", + "clip_rect", + "map", + "refcount" + ] + } + }, + { + "file": "SDL/SDL_events.h", + "defines": [], + "structs": { + "SDL_KeyboardEvent": [ + "type", + "windowID", + "state", + "repeat", + "padding2", + "padding3", + "keysym" + ], + "SDL_TextInputEvent": [ + "type", + "windowID", + "text" + ], + "SDL_MouseMotionEvent": [ + "type", + "windowID", + "state", + "padding1", + "padding2", + "padding3", + "x", + "y", + "xrel", + "yrel" + ], + "SDL_MouseButtonEvent": [ + "type", + "windowID", + "button", + "state", + "padding1", + "padding2", + "x", + "y" + ], + "SDL_ResizeEvent": [ + "type", + "w", + "h" + ] + } + }, + { + "file": "SDL/SDL_audio.h", + "defines": [ + "SDL_AUDIO_MASK_BITSIZE", + "SDL_AUDIO_MASK_DATATYPE", + "SDL_AUDIO_MASK_ENDIAN", + "SDL_AUDIO_MASK_SIGNED", + "AUDIO_U8", + "AUDIO_S8", + "AUDIO_U16LSB", + "AUDIO_S16LSB", + "AUDIO_U16MSB", + "AUDIO_S16MSB", + "AUDIO_U16", + "AUDIO_S16", + "AUDIO_S32LSB", + "AUDIO_S32MSB", + "AUDIO_S32", + "AUDIO_F32LSB", + "AUDIO_F32MSB", + "AUDIO_F32", + "AUDIO_U16SYS", + "AUDIO_S16SYS", + "AUDIO_S32SYS", + "AUDIO_F32SYS", + "SDL_AUDIO_ALLOW_FREQUENCY_CHANGE", + "SDL_AUDIO_ALLOW_FORMAT_CHANGE", + "SDL_AUDIO_ALLOW_CHANNELS_CHANGE", + "SDL_AUDIO_ALLOW_ANY_CHANGE", + "SDL_MIX_MAXVOLUME" + ], + "structs": { + "SDL_AudioCVT": [ + "needed", + "src_format", + "dst_format", + "rate_incr", + "buf", + "len", + "len_cvt", + "len_mult", + "len_ratio", + "filters", + "filter_index" + ], + "SDL_AudioSpec": [ + "freq", + "format", + "channels", + "silence", + "samples", + "padding", + "size", + "callback", + "userdata" + ] + } + }, + { + "file": "SDL/SDL_version.h", + "defines": [ + "SDL_MAJOR_VERSION", + "SDL_MINOR_VERSION", + "SDL_PATCHLEVEL", + "SDL_COMPILEDVERSION" + ], + "structs": { + "SDL_version": [ + "major", + "minor", + "patch" + ] + } + } +] diff --git a/src/utility.js b/src/utility.js index 7d122cef..b793106c 100644 --- a/src/utility.js +++ b/src/utility.js @@ -334,6 +334,17 @@ function jsonCompare(x, y) { return JSON.stringify(x) == JSON.stringify(y); } +function sortedJsonCompare(x, y) { + if (x === null || typeof x !== 'object') return x === y; + for (var i in x) { + if (!sortedJsonCompare(x[i], y[i])) return false; + } + for (var i in y) { + if (!sortedJsonCompare(x[i], y[i])) return false; + } + return true; +} + function stringifyWithFunctions(obj) { if (typeof obj === 'function') return obj.toString(); if (obj === null || typeof obj !== 'object') return JSON.stringify(obj); |