aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xemcc5
-rw-r--r--src/jsifier.js10
-rw-r--r--src/library_browser.js31
-rw-r--r--src/library_glut.js37
-rw-r--r--src/library_sdl.js24
-rw-r--r--tests/cases/returnfp.ll23
-rwxr-xr-xtests/runner.py9
-rw-r--r--tests/sdl_audio_mix_channels.c59
-rw-r--r--tools/eliminator/asm-eliminator-test-output.js5
-rw-r--r--tools/eliminator/asm-eliminator-test.js8
-rw-r--r--tools/js-optimizer.js98
-rw-r--r--tools/js_optimizer.py12
-rw-r--r--tools/shared.py23
-rw-r--r--tools/test-js-optimizer-asm-regs-min.js2
-rw-r--r--tools/test-js-optimizer-asm-relocate-output.js9
-rw-r--r--tools/test-js-optimizer-asm-relocate.js12
16 files changed, 293 insertions, 74 deletions
diff --git a/emcc b/emcc
index b09a2746..0c151575 100755
--- a/emcc
+++ b/emcc
@@ -844,6 +844,7 @@ try:
DEBUG = 1
shared.set_logging()
logging.debug('invocation: ' + ' '.join(sys.argv))
+ shared.apply_configuration() # reset config to pick up change
newargs[i] = ''
elif newargs[i].startswith('--shell-file'):
check_bad_eq(newargs[i])
@@ -1076,7 +1077,7 @@ try:
output_file = in_temp(unsuffixed(uniquename(input_file)) + '.o')
temp_files.append(output_file)
args = newargs + ['-emit-llvm', '-c', input_file, '-o', output_file]
- logging.debug("running:" + call + ' '.join(args))
+ logging.debug("running:" + call + ' ' + ' '.join(args))
execute([call] + args) # let compiler frontend print directly, so colors are saved (PIPE kills that)
if not os.path.exists(output_file):
logging.error('compiler frontend failed to generate LLVM bitcode, halting')
@@ -1574,7 +1575,7 @@ try:
js_optimizer_queue += ['registerize']
if opt_level > 0:
- if debug_level < 2 and shared.Settings.ASM_JS and shared.Settings.RELOOP: js_optimizer_queue += ['minifyGlobals']
+ if debug_level < 2 and shared.Settings.ASM_JS: js_optimizer_queue = map(lambda p: p if p != 'registerize' else 'registerizeAndMinify', js_optimizer_queue)
if debug_level == 0: js_optimizer_queue += ['minifyWhitespace']
if closure and shared.Settings.ASM_JS:
diff --git a/src/jsifier.js b/src/jsifier.js
index a432b3e0..d6cd188c 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -1182,7 +1182,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);
+ var call_ = makeFunctionCall(item.ident, item.params, item.funcData, item.type, ASM_JS && !disabled, !!item.assignTo || !item.standalone);
var ret;
@@ -1339,7 +1339,7 @@ function JSify(data, functionsOnly, givenFunctions) {
return ret;
});
- function makeFunctionCall(ident, params, funcData, type, forceByPointer) {
+ function makeFunctionCall(ident, params, funcData, type, forceByPointer, hasReturn) {
// We cannot compile assembly. See comment in intertyper.js:'Call'
assert(ident != 'asm', 'Inline assembly cannot be compiled to JavaScript!');
@@ -1467,8 +1467,8 @@ function JSify(data, functionsOnly, givenFunctions) {
}
}
- var returnType;
- if (byPointer || ASM_JS) {
+ var returnType = 'void';
+ if ((byPointer || ASM_JS) && hasReturn) {
returnType = getReturnType(type);
}
@@ -1513,7 +1513,7 @@ function JSify(data, functionsOnly, givenFunctions) {
makeFuncLineActor('getelementptr', function(item) { return finalizeLLVMFunctionCall(item) });
makeFuncLineActor('call', function(item) {
if (item.standalone && LibraryManager.isStubFunction(item.ident)) return ';';
- return makeFunctionCall(item.ident, item.params, item.funcData, item.type) + (item.standalone ? ';' : '');
+ return makeFunctionCall(item.ident, item.params, item.funcData, item.type, false, !!item.assignTo || !item.standalone) + (item.standalone ? ';' : '');
});
makeFuncLineActor('unreachable', function(item) {
diff --git a/src/library_browser.js b/src/library_browser.js
index 925b64e2..d007d9a7 100644
--- a/src/library_browser.js
+++ b/src/library_browser.js
@@ -426,8 +426,17 @@ mergeInto(LibraryManager.library, {
Browser.mouseMovementX = Browser.getMovementX(event);
Browser.mouseMovementY = Browser.getMovementY(event);
}
- Browser.mouseX = SDL.mouseX + Browser.mouseMovementX;
- Browser.mouseY = SDL.mouseY + Browser.mouseMovementY;
+
+ // check if SDL is available
+ if (typeof SDL != "undefined") {
+ Browser.mouseX = SDL.mouseX + Browser.mouseMovementX;
+ Browser.mouseY = SDL.mouseY + Browser.mouseMovementY;
+ } else {
+ // just add the mouse delta to the current absolut mouse position
+ // FIXME: ideally this should be clamped against the canvas size and zero
+ Browser.mouseX += Browser.mouseMovementX;
+ Browser.mouseY += Browser.mouseMovementY;
+ }
} else {
// Otherwise, calculate the movement based on the changes
// in the coordinates.
@@ -504,9 +513,12 @@ mergeInto(LibraryManager.library, {
this.windowedHeight = canvas.height;
canvas.width = screen.width;
canvas.height = screen.height;
- var flags = {{{ makeGetValue('SDL.screen+Runtime.QUANTUM_SIZE*0', '0', 'i32', 0, 1) }}};
- flags = flags | 0x00800000; // set SDL_FULLSCREEN flag
- {{{ makeSetValue('SDL.screen+Runtime.QUANTUM_SIZE*0', '0', 'flags', 'i32') }}}
+ // check if SDL is available
+ if (typeof SDL != "undefined") {
+ var flags = {{{ makeGetValue('SDL.screen+Runtime.QUANTUM_SIZE*0', '0', 'i32', 0, 1) }}};
+ flags = flags | 0x00800000; // set SDL_FULLSCREEN flag
+ {{{ makeSetValue('SDL.screen+Runtime.QUANTUM_SIZE*0', '0', 'flags', 'i32') }}}
+ }
Browser.updateResizeListeners();
},
@@ -514,9 +526,12 @@ mergeInto(LibraryManager.library, {
var canvas = Module['canvas'];
canvas.width = this.windowedWidth;
canvas.height = this.windowedHeight;
- var flags = {{{ makeGetValue('SDL.screen+Runtime.QUANTUM_SIZE*0', '0', 'i32', 0, 1) }}};
- flags = flags & ~0x00800000; // clear SDL_FULLSCREEN flag
- {{{ makeSetValue('SDL.screen+Runtime.QUANTUM_SIZE*0', '0', 'flags', 'i32') }}}
+ // check if SDL is available
+ if (typeof SDL != "undefined") {
+ var flags = {{{ makeGetValue('SDL.screen+Runtime.QUANTUM_SIZE*0', '0', 'i32', 0, 1) }}};
+ flags = flags & ~0x00800000; // clear SDL_FULLSCREEN flag
+ {{{ makeSetValue('SDL.screen+Runtime.QUANTUM_SIZE*0', '0', 'flags', 'i32') }}}
+ }
Browser.updateResizeListeners();
}
diff --git a/src/library_glut.js b/src/library_glut.js
index 38cfe55b..36d47787 100644
--- a/src/library_glut.js
+++ b/src/library_glut.js
@@ -117,9 +117,9 @@ var LibraryGLUT = {
if (48 <= keycode && keycode <= 57)
return keycode; // numeric TODO handle shift?
if (65 <= keycode && keycode <= 90)
- return event['shiftKey'] ? keycode : keycode + 32;
+ return event['shiftKey'] ? keycode : keycode + 32;
if (106 <= keycode && keycode <= 111)
- return keycode - 106 + 42; // *,+-./ TODO handle shift?
+ return keycode - 106 + 42; // *,+-./ TODO handle shift?
switch (keycode) {
case 27: // escape
@@ -227,7 +227,7 @@ var LibraryGLUT = {
} else {
width = GLUT.windowWidth;
height = GLUT.windowHeight;
- // TODO set position
+ // TODO set position
document.removeEventListener('fullscreenchange', GLUT.onFullScreenEventChange, true);
document.removeEventListener('mozfullscreenchange', GLUT.onFullScreenEventChange, true);
document.removeEventListener('webkitfullscreenchange', GLUT.onFullScreenEventChange, true);
@@ -255,13 +255,14 @@ var LibraryGLUT = {
document['cancelFullScreen'] ||
document['mozCancelFullScreen'] ||
document['webkitCancelFullScreen'] ||
- (function() {});
+ (function() {});
CFS.apply(document, []);
}
},
glutGetModifiers: function() { return GLUT.modifiers; },
+ glutInit__deps: ['$Browser'],
glutInit: function(argcp, argv) {
// Ignore arguments
GLUT.initTime = Date.now();
@@ -271,6 +272,12 @@ var LibraryGLUT = {
window.addEventListener("mousemove", GLUT.onMousemove, true);
window.addEventListener("mousedown", GLUT.onMouseButtonDown, true);
window.addEventListener("mouseup", GLUT.onMouseButtonUp, true);
+
+ Browser.resizeListeners.push(function(width, height) {
+ if (GLUT.reshapeFunc) {
+ Runtime.dynCall('vii', GLUT.reshapeFunc, [width, height]);
+ }
+ });
__ATEXIT__.push({ func: function() {
window.removeEventListener("keydown", GLUT.onKeydown, true);
@@ -294,21 +301,25 @@ var LibraryGLUT = {
glutGet: function(type) {
switch (type) {
case 100: /* GLUT_WINDOW_X */
- return 0; /* TODO */
+ return 0; /* TODO */
case 101: /* GLUT_WINDOW_Y */
- return 0; /* TODO */
+ return 0; /* TODO */
case 102: /* GLUT_WINDOW_WIDTH */
- return Module['canvas'].width;
+ return Module['canvas'].width;
case 103: /* GLUT_WINDOW_HEIGHT */
- return Module['canvas'].height;
+ return Module['canvas'].height;
+ case 200: /* GLUT_SCREEN_WIDTH */
+ return Module['canvas'].width;
+ case 201: /* GLUT_SCREEN_HEIGHT */
+ return Module['canvas'].height;
case 500: /* GLUT_INIT_WINDOW_X */
- return 0; /* TODO */
+ return 0; /* TODO */
case 501: /* GLUT_INIT_WINDOW_Y */
- return 0; /* TODO */
+ return 0; /* TODO */
case 502: /* GLUT_INIT_WINDOW_WIDTH */
- return GLUT.initWindowWidth;
+ return GLUT.initWindowWidth;
case 503: /* GLUT_INIT_WINDOW_HEIGHT */
- return GLUT.initWindowHeight;
+ return GLUT.initWindowHeight;
case 700: /* GLUT_ELAPSED_TIME */
var now = Date.now();
return now - GLUT.initTime;
@@ -386,10 +397,8 @@ var LibraryGLUT = {
glutReshapeWindow__deps: ['$GLUT', 'glutPostRedisplay'],
glutReshapeWindow: function(width, height) {
GLUT.cancelFullScreen();
- // console.log("glutReshapeWindow: " + width + ", " + height);
Browser.setCanvasSize(width, height);
if (GLUT.reshapeFunc) {
- // console.log("GLUT.reshapeFunc: " + width + ", " + height);
Runtime.dynCall('vii', GLUT.reshapeFunc, [width, height]);
}
_glutPostRedisplay();
diff --git a/src/library_sdl.js b/src/library_sdl.js
index 356c9746..176a2fae 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -1385,26 +1385,28 @@ var LibrarySDL = {
// If the user asks us to allocate a channel automatically, get the first
// free one.
if (channel == -1) {
- channel = SDL.channelMinimumNumber;
for (var i = SDL.channelMinimumNumber; i < SDL.numChannels; i++) {
if (!SDL.channels[i].audio) {
channel = i;
break;
}
}
+ if (channel == -1) {
+ Module.printErr('All ' + SDL.numChannels + ' channels in use!');
+ return -1;
+ }
}
-
// We clone the audio node to utilize the preloaded audio buffer, since
// the browser has already preloaded the audio file.
var channelInfo = SDL.channels[channel];
channelInfo.audio = audio = audio.cloneNode(true);
audio.numChannels = info.audio.numChannels;
audio.frequency = info.audio.frequency;
-
// TODO: handle N loops. Behavior matches Mix_PlayMusic
audio.loop = loops != 0;
- if (SDL.channelFinished) {
- audio['onended'] = function() { // TODO: cache these
+ audio['onended'] = function() { // TODO: cache these
+ channelInfo.audio = null;
+ if (SDL.channelFinished) {
Runtime.getFuncWrapper(SDL.channelFinished, 'vi')(channel);
}
}
@@ -1461,7 +1463,6 @@ var LibrarySDL = {
audio.play();
}
audio.volume = channelInfo.volume;
- audio.paused = false;
return channel;
},
Mix_PlayChannelTimed: 'Mix_PlayChannel', // XXX ignore Timing
@@ -1475,6 +1476,8 @@ var LibrarySDL = {
if (info.audio) {
info.audio.pause();
info.audio = null;
+ } else {
+ Module.printErr('No Audio for channel: ' + channel);
}
if (SDL.channelFinished) {
Runtime.getFuncWrapper(SDL.channelFinished, 'vi')(channel);
@@ -1512,6 +1515,12 @@ var LibrarySDL = {
}
audio.volume = SDL.music.volume;
audio['onended'] = _Mix_HaltMusic; // will send callback
+ if (SDL.music.audio) {
+ if (!SDL.music.audio.paused) {
+ Module.printErr('Music is already playing. ' + SDL.music.source);
+ }
+ SDL.music.audio.pause();
+ }
SDL.music.audio = audio;
return 0;
},
@@ -1577,7 +1586,8 @@ var LibrarySDL = {
var info = SDL.channels[channel];
if (info && info.audio) {
info.audio.pause();
- info.audio.paused = true;
+ } else {
+ Module.printErr('Mix_Pause: no sound found for channel: ' + channel);
}
},
diff --git a/tests/cases/returnfp.ll b/tests/cases/returnfp.ll
new file mode 100644
index 00000000..974459e5
--- /dev/null
+++ b/tests/cases/returnfp.ll
@@ -0,0 +1,23 @@
+; ModuleID = 'tests/hello_world.bc'
+target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128"
+target triple = "i386-pc-linux-gnu"
+
+@.str = private unnamed_addr constant [15 x i8] c"hello, world!\0A\00", align 1 ; [#uses=1 type=[15 x i8]*]
+
+define internal void @__xmlRaiseError(void ()* %schannel) nounwind {
+ %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([15 x i8]* @.str, i32 0, i32 0)) ; [#uses=0 type=i32]
+ ret void
+}
+
+; [#uses=0]
+define i32 @main() {
+entry:
+ %retval = alloca i32, align 4 ; [#uses=1 type=i32*]
+ store i32 0, i32* %retval
+ call void (void ()*)* @__xmlRaiseError(void ()* null)
+ ret i32 1
+}
+
+; [#uses=1]
+declare i32 @printf(i8*, ...)
+
diff --git a/tests/runner.py b/tests/runner.py
index f0f77bc9..c55c96fd 100755
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -11340,6 +11340,8 @@ f.close()
['asm', 'simplifyExpressionsPre']),
(path_from_root('tools', 'test-js-optimizer-asm-last.js'), open(path_from_root('tools', 'test-js-optimizer-asm-last-output.js')).read(),
['asm', 'last']),
+ (path_from_root('tools', 'test-js-optimizer-asm-relocate.js'), open(path_from_root('tools', 'test-js-optimizer-asm-relocate-output.js')).read(),
+ ['asm', 'relocate']),
]:
print input
output = Popen(listify(NODE_JS) + [path_from_root('tools', 'js-optimizer.js'), input] + passes, stdin=PIPE, stdout=PIPE).communicate()[0]
@@ -12547,6 +12549,13 @@ Press any key to continue.'''
Popen([PYTHON, EMCC, '-O2', '--closure', '1', '--minify', '0', os.path.join(self.get_dir(), 'sdl_audio.c'), '--preload-file', 'sound.ogg', '--preload-file', 'sound2.wav', '--preload-file', 'bad.ogg', '-o', 'page.html', '-s', 'EXPORTED_FUNCTIONS=["_main", "_play", "_play2"]']).communicate()
self.run_browser('page.html', '', '/report_result?1')
+ def test_sdl_audio_mix_channels(self):
+ shutil.copyfile(path_from_root('tests', 'sounds', 'noise.ogg'), os.path.join(self.get_dir(), 'sound.ogg'))
+ open(os.path.join(self.get_dir(), 'sdl_audio_mix_channels.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_audio_mix_channels.c')).read()))
+
+ Popen([PYTHON, EMCC, '-O2', '--minify', '0', os.path.join(self.get_dir(), 'sdl_audio_mix_channels.c'), '--preload-file', 'sound.ogg', '-o', 'page.html']).communicate()
+ self.run_browser('page.html', '', '/report_result?1')
+
def test_sdl_audio_mix(self):
shutil.copyfile(path_from_root('tests', 'sounds', 'pluck.ogg'), os.path.join(self.get_dir(), 'sound.ogg'))
shutil.copyfile(path_from_root('tests', 'sounds', 'the_entertainer.ogg'), os.path.join(self.get_dir(), 'music.ogg'))
diff --git a/tests/sdl_audio_mix_channels.c b/tests/sdl_audio_mix_channels.c
new file mode 100644
index 00000000..dd91d594
--- /dev/null
+++ b/tests/sdl_audio_mix_channels.c
@@ -0,0 +1,59 @@
+#include <stdio.h>
+#include <SDL/SDL.h>
+#include <SDL/SDL_mixer.h>
+#include <assert.h>
+#include <emscripten.h>
+
+static Mix_Chunk *sound = NULL;
+static Mix_Chunk *noiseLoop = NULL;
+static Mix_Music *music = NULL;
+
+static int soundChannel = 0;
+static int noiseLoopChannel = 0;
+
+static const int kNumChannels = 40;
+
+static int loadAndPlay()
+{
+ return Mix_PlayChannel(-1, sound, -1);
+}
+
+int main(int argc, char **argv) {
+ SDL_Init(SDL_INIT_AUDIO);
+ Mix_Init(MIX_INIT_OGG);
+
+ int ret = Mix_OpenAudio(0, 0, 0, 0); // we ignore all these..
+ assert(ret == 0);
+
+ Mix_AllocateChannels(kNumChannels);
+
+ sound = Mix_LoadWAV("sound.ogg");
+
+ // allocate all the channels
+ for ( int i = 0; i < kNumChannels; i++ )
+ {
+ assert(loadAndPlay() != -1);
+ }
+
+ // This point, we should have exhausted our channels
+
+
+
+
+ int lastChannel = loadAndPlay();
+
+#if EMSCRIPTEN
+ int result = (lastChannel == -1);
+ REPORT_RESULT();
+#endif
+
+ assert(lastChannel == -1);
+
+ // force a quit
+ while(Mix_Init(0))
+ Mix_Quit();
+ Mix_CloseAudio();
+
+ return 0;
+}
+
diff --git a/tools/eliminator/asm-eliminator-test-output.js b/tools/eliminator/asm-eliminator-test-output.js
index b28fbd4a..5cc6238e 100644
--- a/tools/eliminator/asm-eliminator-test-output.js
+++ b/tools/eliminator/asm-eliminator-test-output.js
@@ -5121,4 +5121,9 @@ function tempDouble2($46, $14, $28, $42, $20, $32, $45) {
HEAP32[$45 + 4 >> 2] = $_sroa_06_0_insert_insert$1;
HEAP32[$45 + 8 >> 2] = $_sroa_06_0_insert_insert$1;
}
+function watIf() {
+ while (1) {
+ if ($cmp38) {} else {}
+ }
+}
diff --git a/tools/eliminator/asm-eliminator-test.js b/tools/eliminator/asm-eliminator-test.js
index f90c3557..b33c6040 100644
--- a/tools/eliminator/asm-eliminator-test.js
+++ b/tools/eliminator/asm-eliminator-test.js
@@ -6851,5 +6851,11 @@ function tempDouble2($46, $14, $28, $42, $20, $32, $45) {
HEAP32[$45 + 4 >> 2] = $_sroa_06_0_insert_insert$1;
HEAP32[$45 + 8 >> 2] = $_sroa_06_0_insert_insert$1;
}
-// EMSCRIPTEN_GENERATED_FUNCTIONS: ["asm", "__Z11printResultPiS_j", "_segment_holding", "__ZN5identC2EiPKcPci", "_vec2Length", "exc", "label", "confuusion", "tempDouble", "_org_apache_harmony_luni_util_NumberConverter_freeFormat__", "__ZN23b2EdgeAndPolygonContact8EvaluateEP10b2ManifoldRK11b2TransformS4_", "_java_nio_charset_Charset_forNameInternal___java_lang_String", "looop2", "looop3", "looop4", "looop5", "looop6", "looop7", "looop8", "multiloop", "multiloop2", "tempDouble2"]
+function watIf() {
+ while (1) {
+ if ($cmp38) {} else {
+ }
+ }
+}
+// EMSCRIPTEN_GENERATED_FUNCTIONS: ["asm", "__Z11printResultPiS_j", "_segment_holding", "__ZN5identC2EiPKcPci", "_vec2Length", "exc", "label", "confuusion", "tempDouble", "_org_apache_harmony_luni_util_NumberConverter_freeFormat__", "__ZN23b2EdgeAndPolygonContact8EvaluateEP10b2ManifoldRK11b2TransformS4_", "_java_nio_charset_Charset_forNameInternal___java_lang_String", "looop2", "looop3", "looop4", "looop5", "looop6", "looop7", "looop8", "multiloop", "multiloop2", "tempDouble2", "watIf"]
diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index 532dc7fc..c772f667 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -144,7 +144,7 @@ var FALSE_NODE = ['unary-prefix', '!', ['num', 1]];
var GENERATED_FUNCTIONS_MARKER = '// EMSCRIPTEN_GENERATED_FUNCTIONS';
var generatedFunctions = false; // whether we have received only generated functions
-var minifierInfo = null;
+var extraInfo = null;
function srcToAst(src) {
return uglify.parser.parse(src, false, debug);
@@ -1638,7 +1638,7 @@ function denormalizeAsm(func, data) {
// Very simple 'registerization', coalescing of variables into a smaller number,
// as part of minification. Globals-level minification began in a previous pass,
-// we receive minifierInfo which tells us how to rename globals. (Only in asm.js.)
+// we receive extraInfo which tells us how to rename globals. (Only in asm.js.)
//
// We do not optimize when there are switches, so this pass only makes sense with
// relooping.
@@ -1680,7 +1680,7 @@ function registerize(ast) {
}
});
vacuum(fun);
- if (minifierInfo) {
+ if (extraInfo) {
assert(asm);
var usedGlobals = {};
var nextLocal = 0;
@@ -1688,7 +1688,7 @@ function registerize(ast) {
traverse(fun, function(node, type) {
if (type === 'name') {
var name = node[1];
- var minified = minifierInfo.globals[name];
+ var minified = extraInfo.globals[name];
if (minified) {
assert(!localVars[name], name); // locals must not shadow globals, or else we don't know which is which
if (localVars[minified]) {
@@ -1721,8 +1721,8 @@ function registerize(ast) {
}
}
});
- assert(fun[1] in minifierInfo.globals, fun[1]);
- fun[1] = minifierInfo.globals[fun[1]];
+ assert(fun[1] in extraInfo.globals, fun[1]);
+ fun[1] = extraInfo.globals[fun[1]];
assert(fun[1]);
var nextRegName = 0;
}
@@ -1730,14 +1730,14 @@ function registerize(ast) {
function getNewRegName(num, name) {
if (!asm) return 'r' + num;
var type = asmData.vars[name];
- if (!minifierInfo) {
+ if (!extraInfo) {
var ret = (type ? 'd' : 'i') + num;
regTypes[ret] = type;
return ret;
}
// find the next free minified name that is not used by a global that shows up in this function
- while (nextRegName < minifierInfo.names.length) {
- var ret = minifierInfo.names[nextRegName++];
+ while (nextRegName < extraInfo.names.length) {
+ var ret = extraInfo.names[nextRegName++];
if (!usedGlobals[ret]) {
regTypes[ret] = type;
return ret;
@@ -2540,13 +2540,13 @@ function eliminate(ast, memSafe) {
var ifTrue = last[2];
var ifFalse = last[3];
var flip = false;
- if (ifFalse[1][0][0] === 'break') { // canonicalize break in the if
+ if (ifFalse[1][0] && ifFalse[1][0][0] === 'break') { // canonicalize break in the if
var temp = ifFalse;
ifFalse = ifTrue;
ifTrue = temp;
flip = true;
}
- if (ifTrue[1][0][0] === 'break') {
+ if (ifTrue[1][0] && ifTrue[1][0][0] === 'break') {
var assigns = ifFalse[1];
var loopers = [], helpers = [];
for (var i = 0; i < assigns.length; i++) {
@@ -2696,16 +2696,16 @@ function minifyGlobals(ast) {
var vars = node[1];
for (var i = 0; i < vars.length; i++) {
var name = vars[i][0];
- assert(next < minifierInfo.names.length);
- vars[i][0] = minified[name] = minifierInfo.names[next++];
+ assert(next < extraInfo.names.length);
+ vars[i][0] = minified[name] = extraInfo.names[next++];
}
}
});
// add all globals in function chunks, i.e. not here but passed to us
- for (var i = 0; i < minifierInfo.globals.length; i++) {
- name = minifierInfo.globals[i];
- assert(next < minifierInfo.names.length);
- minified[name] = minifierInfo.names[next++];
+ for (var i = 0; i < extraInfo.globals.length; i++) {
+ name = extraInfo.globals[i];
+ assert(next < extraInfo.names.length);
+ minified[name] = extraInfo.names[next++];
}
// apply minification
traverse(ast, function(node, type) {
@@ -2716,9 +2716,64 @@ function minifyGlobals(ast) {
}
}
});
- suffix = '// MINIFY_INFO:' + JSON.stringify(minified);
+ suffix = '// EXTRA_INFO:' + JSON.stringify(minified);
}
+// Relocation pass for a shared module (for the functions part of the module)
+//
+// 1. Replace function names with alternate names as defined (to avoid colliding with
+// names in the main module we are being linked to)
+// 2. Hardcode function table offsets from F_BASE+x to const+x if x is a variable, or
+// the constant sum of the base + offset
+// 3. Hardcode heap offsets from H_BASE as well
+function relocate(ast) {
+ assert(asm); // we also assume we are normalized
+
+ var replacements = extraInfo.replacements;
+ var fBase = extraInfo.fBase;
+ var hBase = extraInfo.hBase;
+
+ traverse(ast, function(node, type) {
+ switch(type) {
+ case 'name': case 'defun': {
+ var rep = replacements[node[1]];
+ if (rep) node[1] = rep;
+ break;
+ }
+ case 'binary': {
+ if (node[1] == '+' && node[2][0] == 'name') {
+ var base = null;
+ if (node[2][1] == 'F_BASE') {
+ base = fBase;
+ } else if (node[2][1] == 'H_BASE') {
+ base = hBase;
+ }
+ if (base) {
+ var other = node[3];
+ if (other[0] == 'num') {
+ other[1] += base;
+ return other;
+ } else {
+ node[2] = ['num', base];
+ }
+ }
+ }
+ break;
+ }
+ case 'var': {
+ var vars = node[1];
+ for (var i = 0; i < vars.length; i++) {
+ var name = vars[i][0];
+ assert(!(name in replacements)); // cannot shadow functions we are replacing TODO: fix that
+ }
+ break;
+ }
+ }
+ });
+}
+
+// Last pass utilities
+
// Change +5 to DOT$ZERO(5). We then textually change 5 to 5.0 (uglify's ast cannot differentiate between 5 and 5.0 directly)
function prepDotZero(ast) {
traverse(ast, function(node, type) {
@@ -2783,6 +2838,7 @@ var passes = {
eliminate: eliminate,
eliminateMemSafe: eliminateMemSafe,
minifyGlobals: minifyGlobals,
+ relocate: relocate,
minifyWhitespace: function() { minifyWhitespace = true },
noPrintMetadata: function() { printMetadata = false },
asm: function() { asm = true },
@@ -2805,9 +2861,9 @@ var src = read(arguments_[0]);
var ast = srcToAst(src);
//printErr(JSON.stringify(ast)); throw 1;
generatedFunctions = src.indexOf(GENERATED_FUNCTIONS_MARKER) >= 0;
-var minifierInfoStart = src.indexOf('// MINIFY_INFO:')
-if (minifierInfoStart > 0) minifierInfo = JSON.parse(src.substr(minifierInfoStart + 15));
-//printErr(JSON.stringify(minifierInfo));
+var extraInfoStart = src.indexOf('// EXTRA_INFO:')
+if (extraInfoStart > 0) extraInfo = JSON.parse(src.substr(extraInfoStart + 14));
+//printErr(JSON.stringify(extraInfo));
arguments_.slice(1).forEach(function(arg) {
diff --git a/tools/js_optimizer.py b/tools/js_optimizer.py
index 256c03cf..905ae835 100644
--- a/tools/js_optimizer.py
+++ b/tools/js_optimizer.py
@@ -36,7 +36,7 @@ class Minifier:
MAX_NAMES = 80000
INVALID_2 = set(['do', 'if', 'in'])
- INVALID_3 = set(['for', 'new', 'try', 'var', 'env'])
+ INVALID_3 = set(['for', 'new', 'try', 'var', 'env', 'let'])
self.names = []
init_possibles = string.ascii_letters + '_$'
@@ -73,7 +73,7 @@ class Minifier:
f = open(temp_file, 'w')
f.write(shell)
f.write('\n')
- f.write('// MINIFY_INFO:' + self.serialize())
+ f.write('// EXTRA_INFO:' + self.serialize())
f.close()
output = subprocess.Popen(self.js_engine +
@@ -84,7 +84,7 @@ class Minifier:
assert len(output) > 0 and not output.startswith('Assertion failed'), 'Error in js optimizer: ' + output
#print >> sys.stderr, "minified SHELL 3333333333333333", output, "\n44444444444444444444"
- code, metadata = output.split('// MINIFY_INFO:')
+ code, metadata = output.split('// EXTRA_INFO:')
self.globs = json.loads(metadata)
return code.replace('13371337', '0.0')
@@ -135,9 +135,9 @@ def run_on_js(filename, passes, js_engine, jcache, source_map=False):
end_funcs = js.rfind(end_funcs_marker)
#assert (start_funcs >= 0) == (end_funcs >= 0) == (not not suffix)
- minify_globals = 'minifyGlobals' in passes and 'registerize' in passes and 'asm' in passes
+ minify_globals = 'registerizeAndMinify' in passes and 'asm' in passes
if minify_globals:
- passes = filter(lambda p: p != 'minifyGlobals', passes) # we will run it manually
+ passes = map(lambda p: p if p != 'registerizeAndMinify' else 'registerize', passes)
start_asm_marker = '// EMSCRIPTEN_START_ASM\n'
end_asm_marker = '// EMSCRIPTEN_END_ASM\n'
start_asm = js.find(start_asm_marker)
@@ -250,7 +250,7 @@ EMSCRIPTEN_FUNCS();
f.write(suffix_marker)
if minify_globals:
f.write('\n')
- f.write('// MINIFY_INFO:' + minify_info)
+ f.write('// EXTRA_INFO:' + minify_info)
f.close()
return temp_file
filenames = [write_chunk(chunks[i], i) for i in range(len(chunks))]
diff --git a/tools/shared.py b/tools/shared.py
index 64ffe29f..2a754d27 100644
--- a/tools/shared.py
+++ b/tools/shared.py
@@ -420,7 +420,7 @@ FILE_PACKAGER = path_from_root('tools', 'file_packager.py')
# Temp dir. Create a random one, unless EMCC_DEBUG is set, in which case use TEMP_DIR/emscripten_temp
class Configuration:
- def __init__(self, environ):
+ def __init__(self, environ=os.environ):
self.DEBUG = environ.get('EMCC_DEBUG')
if self.DEBUG == "0":
self.DEBUG = None
@@ -448,11 +448,14 @@ class Configuration:
tmp=self.TEMP_DIR if not self.DEBUG else self.EMSCRIPTEN_TEMP_DIR,
save_debug_files=os.environ.get('EMCC_DEBUG_SAVE'))
-configuration = Configuration(environ=os.environ)
-DEBUG = configuration.DEBUG
-EMSCRIPTEN_TEMP_DIR = configuration.EMSCRIPTEN_TEMP_DIR
-DEBUG_CACHE = configuration.DEBUG_CACHE
-CANONICAL_TEMP_DIR = configuration.CANONICAL_TEMP_DIR
+def apply_configuration():
+ global configuration, DEBUG, EMSCRIPTEN_TEMP_DIR, DEBUG_CACHE, CANONICAL_TEMP_DIR
+ configuration = Configuration()
+ DEBUG = configuration.DEBUG
+ EMSCRIPTEN_TEMP_DIR = configuration.EMSCRIPTEN_TEMP_DIR
+ DEBUG_CACHE = configuration.DEBUG_CACHE
+ CANONICAL_TEMP_DIR = configuration.CANONICAL_TEMP_DIR
+apply_configuration()
logging.basicConfig(format='%(levelname)-8s %(name)s: %(message)s')
def set_logging():
@@ -462,9 +465,11 @@ set_logging()
if not EMSCRIPTEN_TEMP_DIR:
EMSCRIPTEN_TEMP_DIR = tempfile.mkdtemp(prefix='emscripten_temp_', dir=configuration.TEMP_DIR)
- def clean_temp():
- try_delete(EMSCRIPTEN_TEMP_DIR)
- atexit.register(clean_temp)
+ def prepare_to_clean_temp(d):
+ def clean_temp():
+ try_delete(d)
+ atexit.register(clean_temp)
+ prepare_to_clean_temp(EMSCRIPTEN_TEMP_DIR) # this global var might change later
# EM_CONFIG stuff
diff --git a/tools/test-js-optimizer-asm-regs-min.js b/tools/test-js-optimizer-asm-regs-min.js
index f47dbff3..a5b9427e 100644
--- a/tools/test-js-optimizer-asm-regs-min.js
+++ b/tools/test-js-optimizer-asm-regs-min.js
@@ -34,4 +34,4 @@ function collideLocal(i1) {
bGlobal(i1);
}
// EMSCRIPTEN_GENERATED_FUNCTIONS
-// MINIFY_INFO: { "names": ["a", "b", "c", "d", "e", "f", "g", "h", "i", "i1", "cl"], "globals": { "aGlobal": "a", "bGlobal": "i1", "collideLocal": "cl" } }
+// EXTRA_INFO: { "names": ["a", "b", "c", "d", "e", "f", "g", "h", "i", "i1", "cl"], "globals": { "aGlobal": "a", "bGlobal": "i1", "collideLocal": "cl" } }
diff --git a/tools/test-js-optimizer-asm-relocate-output.js b/tools/test-js-optimizer-asm-relocate-output.js
new file mode 100644
index 00000000..6a197e81
--- /dev/null
+++ b/tools/test-js-optimizer-asm-relocate-output.js
@@ -0,0 +1,9 @@
+function leaveMeAlone(c) {}
+function fixed(a, b) {}
+function a(x, y) {
+ fixed(34, 12);
+ fixed(34 | 0, 12 | 0);
+ leaveMeAlone(10 + x, 33 + y);
+ leaveMeAlone(10 + x | 0, 33 + y | 0);
+}
+
diff --git a/tools/test-js-optimizer-asm-relocate.js b/tools/test-js-optimizer-asm-relocate.js
new file mode 100644
index 00000000..a45bc2f0
--- /dev/null
+++ b/tools/test-js-optimizer-asm-relocate.js
@@ -0,0 +1,12 @@
+function leaveMeAlone(c) {
+}
+function replaceMe(a, b) {
+}
+function a(x, y) {
+ replaceMe(H_BASE + 1, F_BASE + 2);
+ replaceMe(H_BASE + 1 | 0, F_BASE + 2 | 0);
+ leaveMeAlone(F_BASE + x, H_BASE + y);
+ leaveMeAlone(F_BASE + x | 0, H_BASE + y | 0);
+}
+// EMSCRIPTEN_GENERATED_FUNCTIONS
+// EXTRA_INFO: { "replacements": { "replaceMe": "fixed" }, "hBase": 33, "fBase": 10 }