aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/intertyper.js21
-rw-r--r--src/jsifier.js51
-rw-r--r--src/library.js15
-rw-r--r--src/parseTools.js60
-rw-r--r--tests/runner.py16
5 files changed, 103 insertions, 60 deletions
diff --git a/src/intertyper.js b/src/intertyper.js
index 9e3f7f1e..715b6fa4 100644
--- a/src/intertyper.js
+++ b/src/intertyper.js
@@ -1,6 +1,8 @@
// LLVM assembly => internal intermediate representation, which is ready
// to be processed by the later stages.
+var tokenizer; // TODO: Clean this up/out
+
//! @param parseFunctions We parse functions only on later passes, since we do not
//! want to parse all of them at once, and have all their
//! lines and data in memory at the same time.
@@ -92,7 +94,7 @@ function intertyper(data, parseFunctions, baseLineNum) {
};
// Line tokenizer
- var tokenizer = substrate.addActor('Tokenizer', {
+ tokenizer = substrate.addActor('Tokenizer', {
processItem: function(item, inner) {
//assert(item.lineNum != 40000);
//if (item.lineNum) print(item.lineNum);
@@ -327,7 +329,7 @@ function intertyper(data, parseFunctions, baseLineNum) {
// Handle a single segment (after comma separation)
function handleSegment(segment) {
if (segment[1].text == 'null') {
- return { intertype: 'value', value: 0 };
+ return { intertype: 'value', value: 0, type: 'i32' };
} else if (segment[1].text == 'zeroinitializer') {
return { intertype: 'emptystruct', type: segment[0].text };
} else if (segment[1].text in PARSABLE_LLVM_FUNCTIONS) {
@@ -339,12 +341,12 @@ function intertyper(data, parseFunctions, baseLineNum) {
} else if (segment[1].type && segment[1].type == '[') {
return { intertype: 'list', type: segment[0].text, contents: handleSegments(segment[1].item.tokens) };
} else if (segment.length == 2) {
- return { intertype: 'value', value: toNiceIdent(segment[1].text) };
+ return { intertype: 'value', type: segment[0].text, value: toNiceIdent(segment[1].text) };
} else if (segment[1].text === 'c') {
// string
var text = segment[2].text;
text = text.substr(1, text.length-2);
- return { intertype: 'string', text: text };
+ return { intertype: 'string', text: text, type: 'i8*' };
} else if (segment[1].text === 'blockaddress') {
return parseBlockAddress(segment);
} else {
@@ -636,15 +638,17 @@ function intertyper(data, parseFunctions, baseLineNum) {
processItem: function(item) {
item.intertype = 'phi';
item.type = item.tokens[1].text;
+ var typeToken = [item.tokens[1]];
Types.needAnalysis[item.type] = 0;
var last = getTokenIndexByText(item.tokens, ';');
item.params = splitTokenList(item.tokens.slice(2, last)).map(function(segment) {
var subSegments = splitTokenList(segment[0].item.tokens);
- return {
+ var ret = {
intertype: 'phiparam',
label: toNiceIdent(subSegments[1][0].text),
- value: parseLLVMSegment(subSegments[0])
+ value: parseLLVMSegment(typeToken.concat(subSegments[0]))
};
+ return ret;
});
this.forwardItem(item, 'Reintegrator');
}
@@ -675,6 +679,9 @@ function intertyper(data, parseFunctions, baseLineNum) {
} else {
item.type = item.param1.type;
}
+ for (var i = 1; i <= 4; i++) {
+ if (item['param'+i]) item['param'+i].type = item.type; // All params have the same type
+ }
Types.needAnalysis[item.type] = 0;
this.forwardItem(item, 'Reintegrator');
}
@@ -726,7 +733,7 @@ function intertyper(data, parseFunctions, baseLineNum) {
return [{
intertype: 'return',
type: type,
- value: (item.tokens[2] && type !== 'void') ? parseLLVMSegment(item.tokens.slice(2)) : null,
+ value: (item.tokens[2] && type !== 'void') ? parseLLVMSegment(item.tokens.slice(1)) : null,
lineNum: item.lineNum
}];
}
diff --git a/src/jsifier.js b/src/jsifier.js
index 62acf8d7..7784c1ab 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -3,8 +3,10 @@
// Main function
function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) {
+ var mainPass = !functionsOnly;
+
// Add additional necessary items for the main pass
- if (!functionsOnly) {
+ if (mainPass) {
var libFuncsToInclude;
if (INCLUDE_FULL_LIBRARY) {
assert(!BUILD_AS_SHARED_LIB, 'Cannot have both INCLUDE_FULL_LIBRARY and BUILD_AS_SHARED_LIB set.')
@@ -31,11 +33,12 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) {
substrate = new Substrate('JSifyer');
- var GLOBAL_VARIABLES = functionsOnly ? givenGlobalVariables : data.globalVariables;
+ var GLOBAL_VARIABLES = !mainPass ? givenGlobalVariables : data.globalVariables;
+
+ Functions.currFunctions = !mainPass ? givenFunctions : {};
- Functions.currFunctions = functionsOnly ? givenFunctions : {};
- // Now that analysis has completed, we can get around to handling unparsedFunctions
- (functionsOnly ? data.functions : data.unparsedFunctions.concat(data.functions)).forEach(function(func) {
+ // Now that first-pass analysis has completed (so we have basic types, etc.), we can get around to handling unparsedFunctions
+ (!mainPass ? data.functions : data.unparsedFunctions.concat(data.functions)).forEach(function(func) {
// Save just what we need, to save memory
Functions.currFunctions[func.ident] = {
hasVarArgs: func.hasVarArgs,
@@ -57,7 +60,11 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) {
if (data.unparsedFunctions.length > 0) {
// We are now doing the final JS generation
dprint('unparsedFunctions', '== Completed unparsedFunctions ==\n');
- //Debugging.clear(); // Save some memory, before the final heavy lifting
+
+ // Save some memory, before the final heavy lifting
+ //Functions.currFunctions = null;
+ //Functions.currExternalFunctions = null;
+ //Debugging.clear();
}
// Actors
@@ -117,7 +124,7 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) {
if (value.intertype in PARSABLE_LLVM_FUNCTIONS) {
return [finalizeLLVMFunctionCall(value)];
} else if (Runtime.isNumberType(type) || pointingLevels(type) >= 1) {
- return indexizeFunctions(parseNumerical(value.value));
+ return indexizeFunctions(parseNumerical(value.value), type);
} else if (value.intertype === 'emptystruct') {
return makeEmptyStruct(type);
} else if (value.intertype === 'string') {
@@ -128,23 +135,26 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) {
function handleSegments(tokens) {
// Handle a single segment (after comma separation)
function handleSegment(segment) {
+ var ret;
if (segment.intertype === 'value') {
- return segment.value.toString();
+ ret = segment.value.toString();
} else if (segment.intertype === 'emptystruct') {
- return makeEmptyStruct(segment.type);
+ ret = makeEmptyStruct(segment.type);
} else if (segment.intertype in PARSABLE_LLVM_FUNCTIONS) {
- return finalizeLLVMFunctionCall(segment);
+ ret = finalizeLLVMFunctionCall(segment);
} else if (segment.intertype in set('struct', 'list')) {
- return alignStruct(handleSegments(segment.contents), segment.type);
+ ret = alignStruct(handleSegments(segment.contents), segment.type);
} else if (segment.intertype === 'string') {
- return parseLLVMString(segment.text); // + ' /* ' + text + '*/';
+ ret = parseLLVMString(segment.text); // + ' /* ' + text + '*/';
} else if (segment.intertype === 'blockaddress') {
- return finalizeBlockAddress(segment);
+ ret = finalizeBlockAddress(segment);
} else {
throw 'Invalid segment: ' + dump(segment);
}
+ assert(segment.type, 'Missing type for constant segment: ' + dump(segment));
+ return indexizeFunctions(ret, segment.type);
};
- return tokens.map(handleSegment).map(indexizeFunctions);
+ return tokens.map(handleSegment)
}
return alignStruct(handleSegments(value.contents), type);
}
@@ -292,7 +302,7 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) {
}
item.JS = addFromLibrary(shortident);
} else {
- item.JS = '// stub for ' + item.ident;
+ item.JS = 'var ' + item.ident + '; // stub for ' + item.ident;
}
return ret;
}
@@ -720,6 +730,7 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) {
var func = Functions.currFunctions[ident];
var args = [];
+ var argsTypes = [];
var varargs = [];
var varargsTypes = [];
@@ -727,6 +738,7 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) {
var val = finalizeParam(param);
if (!func || !func.hasVarArgs || i < func.numParams-1) { // unrecognized functions (like library ones) cannot have varargs
args.push(val);
+ argsTypes.push(param.type);
} else {
varargs.push(val);
varargs = varargs.concat(zeros(getNativeFieldSize(param.type)-1));
@@ -735,8 +747,9 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) {
}
});
- args = args.map(indexizeFunctions);
- varargs = varargs.map(indexizeFunctions);
+ args = args.map(function(arg, i) { return indexizeFunctions(arg, argsTypes[i]) });
+ varargs = varargs.map(function(vararg, i) { return vararg === 0 ? 0 : indexizeFunctions(vararg, varargsTypes[i]) });
+
if (func && func.hasVarArgs) {
if (varargs.length === 0) {
varargs = [0];
@@ -771,12 +784,12 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) {
items = null;
var generated = [];
- if (!functionsOnly) {
+ if (mainPass) {
generated = generated.concat(itemsDict.type).concat(itemsDict.GlobalVariableStub).concat(itemsDict.functionStub);
}
generated = generated.concat(itemsDict.function).concat(data.unparsedFunctions);
- if (functionsOnly) return generated.map(function(item) { return item.JS }).join('\n');
+ if (!mainPass) return generated.map(function(item) { return item.JS }).join('\n');
// We are ready to print out the data, but must do so carefully - we are
// dealing with potentially *huge* strings. Convenient replacements and
diff --git a/src/library.js b/src/library.js
index 284f093d..685f839a 100644
--- a/src/library.js
+++ b/src/library.js
@@ -1476,7 +1476,17 @@ var Library = {
// ==========================================================================
- // dlfcn.h
+ // dlfcn.h - Dynamic library loading
+ //
+ // Some limitations:
+ //
+ // * Minification on each file separately may not work, as they will
+ // have different shortened names. You can in theory combine them, then
+ // minify, then split... perhaps.
+ //
+ // * LLVM optimizations may fail. If the child wants to access a function
+ // in the parent, LLVM opts may remove it from the parent when it is
+ // being compiled. Not sure how to tell LLVM to not do so.
// ==========================================================================
// Data for dlfcn.h.
@@ -1510,6 +1520,9 @@ var Library = {
try {
var lib_module = eval(lib_data)(FUNCTION_TABLE.length);
} catch (e) {
+#if ASSERTIONS
+ print('Error in loading dynamic library: ' + e);
+#endif
DLFCN_DATA.isError = true;
return 0;
}
diff --git a/src/parseTools.js b/src/parseTools.js
index 4f0cda35..debfb6da 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -144,11 +144,10 @@ function isFunctionDef(token) {
function isFunctionType(type) {
var parts = type.split(' ');
- if (parts.length != 2) return false;
if (pointingLevels(type) !== 1) return false;
- var text = removeAllPointing(parts[1]);
- var ret = isType(parts[0]) && isFunctionDef({ text: text, item: {tokens: [{text: text.substr(1, text.length-2)}]} });
- return ret;
+ var text = removeAllPointing(parts.slice(1).join(' '));
+ if (!text) return false;
+ return isType(parts[0]) && isFunctionDef({ text: text, item: tokenizer.processItem({ lineText: text.substr(1, text.length-2) }, true) });
}
function isType(type) { // TODO!
@@ -346,13 +345,20 @@ function finalizeParam(param) {
function parseLLVMSegment(segment) {
var type;
if (segment.length == 1) {
- type = isType(segment[0].text) ? segment[0].text : '?';
- Types.needAnalysis[type] = 0;
- return {
- intertype: 'value',
- ident: toNiceIdent(segment[0].text),
- type: type
- };
+ if (isType(segment[0].text)) {
+ Types.needAnalysis[segment[0].text] = 0;
+ return {
+ intertype: 'type',
+ ident: toNiceIdent(segment[0].text),
+ type: segment[0].text
+ };
+ } else {
+ return {
+ intertype: 'value',
+ ident: toNiceIdent(segment[0].text),
+ type: 'i32'
+ };
+ }
} else if (segment[1].type && segment[1].type == '{') {
type = segment[0].text;
Types.needAnalysis[type] = 0;
@@ -694,27 +700,16 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned) {
}
}
-function indexizeFunctions(value) {
- if (value in Functions.currFunctions) {
+function indexizeFunctions(value, type) {
+ assert((type && type !== '?') || (typeof value === 'string' && value.substr(0, 6) === 'CHECK_'), 'No type given for function indexizing: ' + [value, type]);
+ assert(value !== type, 'Type set to value: ' + [value, type]);
+ if (type && isFunctionType(type) && value[0] === '_') { // checking for _ differentiates from $ (local vars)
if (BUILD_AS_SHARED_LIB) {
return '(FUNCTION_TABLE_OFFSET + ' + Functions.getIndex(value) + ')';
} else {
return Functions.getIndex(value);
}
}
- if (value && value[0] && value[0] == '_') {
- var rootIdent = LibraryManager.getRootIdent(value.slice(1));
- if (!rootIdent) return value;
- if (typeof Library[rootIdent] === 'function') {
- return Functions.getIndex('_' + rootIdent);
- } else if (rootIdent.substr(0, 5) === 'Math.') {
- // Library[..] can be a string, in which case we apply that string. There is one
- // case where this can be a function: Math.*, since we try to optimize those as much
- // as possible. In other words, we don't want to have a wrapper function(x) return Math.sqrt(x).
- // If other functions are deemed important as well, we will need to add them here.
- return Functions.getIndex(rootIdent);
- }
- }
return value;
}
@@ -738,7 +733,7 @@ function makeSetValue(ptr, pos, value, type, noNeedFirst, ignore) {
return ret.join('; ');
}
- value = indexizeFunctions(value);
+ value = indexizeFunctions(value, type);
var offset = calcFastOffset(ptr, pos, noNeedFirst);
if (SAFE_HEAP) {
if (type !== 'null' && type[0] !== '#') type = '"' + safeQuote(type) + '"';
@@ -1046,7 +1041,7 @@ function finalizeLLVMParameter(param) {
if (isNumber(param)) {
return param;
} else if (typeof param === 'string') {
- ret = toNiceIdentCarefully(param);
+ return toNiceIdentCarefully(param);
} else if (param.intertype in PARSABLE_LLVM_FUNCTIONS) {
ret = finalizeLLVMFunctionCall(param);
} else if (param.intertype == 'value') {
@@ -1055,10 +1050,13 @@ function finalizeLLVMParameter(param) {
ret = param.values.map(finalizeLLVMParameter);
} else if (param.intertype === 'blockaddress') {
return finalizeBlockAddress(param);
+ } else if (param.intertype === 'type') {
+ return param.ident; // we don't really want the type here
} else {
throw 'invalid llvm parameter: ' + param.intertype;
}
- return indexizeFunctions(ret);
+ assert(param.type || (typeof param === 'string' && param.substr(0, 6) === 'CHECK_'), 'Missing type for param: ' + dump(param));
+ return indexizeFunctions(ret, param.type);
}
function makeSignOp(value, type, op) {
@@ -1264,8 +1262,6 @@ function processMathop(item) { with(item) {
// TODO: Use this in analyzer, possibly also in jsifier
function walkInterdata(item, pre, post, obj) {
if (!item || !item.intertype) return false;
-//print(' walk: ' + [item.lineNum, item.intertype]);
-//if (item.intertype === 'value') print(dump(item));
if (pre(item, obj)) return true;
var originalObj = obj;
if (obj.replaceWith) obj = obj.replaceWith; // allow pre to replace the object we pass to all its children
@@ -1283,7 +1279,7 @@ function walkInterdata(item, pre, post, obj) {
}
function parseBlockAddress(segment) {
- return { intertype: 'blockaddress', func: toNiceIdent(segment[2].item.tokens[0].text), label: toNiceIdent(segment[2].item.tokens[2].text) };
+ return { intertype: 'blockaddress', func: toNiceIdent(segment[2].item.tokens[0].text), label: toNiceIdent(segment[2].item.tokens[2].text), type: 'i32' };
}
function finalizeBlockAddress(param) {
diff --git a/tests/runner.py b/tests/runner.py
index 4b433b13..fdf15d44 100644
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -1924,14 +1924,24 @@ if 'benchmark' not in sys.argv:
output_nicerizer=lambda x: x.replace('\n', '*'))
def test_dlfcn_data_and_fptr(self):
+ global LLVM_OPTS
+ if LLVM_OPTS: return self.skip() # LLVM opts will optimize out parent_func
+
global BUILD_AS_SHARED_LIB, EXPORTED_FUNCTIONS, EXPORTED_GLOBALS
lib_src = '''
#include <stdio.h>
int global = 42;
+ extern void parent_func(); // a function that is defined in the parent
+
void lib_fptr() {
printf("Second calling lib_fptr from main.\\n");
+ parent_func();
+ // call it also through a pointer, to check indexizing
+ void (*p_f)();
+ p_f = parent_func;
+ p_f();
}
void (*func(int x, void(*fptr)()))() {
@@ -1956,6 +1966,10 @@ if 'benchmark' not in sys.argv:
FUNCTYPE func;
+ void parent_func() {
+ printf("parent_func called from child\\n");
+ }
+
void main_fptr() {
printf("First calling main_fptr from lib.\\n");
}
@@ -1997,7 +2011,7 @@ if 'benchmark' not in sys.argv:
BUILD_AS_SHARED_LIB = 0
EXPORTED_FUNCTIONS = ['_main']
EXPORTED_GLOBALS = []
- self.do_test(src, 'In func: 13*First calling main_fptr from lib.*Second calling lib_fptr from main.*Var: 42*',
+ self.do_test(src, 'In func: 13*First calling main_fptr from lib.*Second calling lib_fptr from main.*parent_func called from child*parent_func called from child*Var: 42*',
output_nicerizer=lambda x: x.replace('\n', '*'))
def test_strtod(self):