summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS1
-rwxr-xr-xemcc230
-rw-r--r--src/library.js20
-rw-r--r--src/library_gl.js7
-rw-r--r--src/library_sdl.js101
-rw-r--r--src/parseTools.js3
-rw-r--r--src/postamble.js4
-rw-r--r--src/preamble.js4
-rw-r--r--src/shell.html30
-rw-r--r--system/include/libc/stdio.h9
-rw-r--r--tests/cases/uadd_overflow.ll25
-rw-r--r--tests/cases/uadd_overflow.txt1
-rw-r--r--tests/freetype/main_2.c135
-rw-r--r--tests/freetype/ref_2.txt33
-rw-r--r--tests/hello_world_sdl.cpp4
-rwxr-xr-xtests/runner.py204
-rw-r--r--tests/sdl_audio.c50
-rw-r--r--tests/sdl_canvas.c8
-rw-r--r--tests/sdl_mouse.c2
-rw-r--r--tests/sounds/LICENSE.txt19
-rw-r--r--tests/sounds/alarmcreatemiltaryfoot_1.wavbin0 -> 443856 bytes
-rw-r--r--tests/sounds/alarmvictory_1.oggbin0 -> 56490 bytes
-rw-r--r--tools/eliminator/eliminator.coffee15
-rw-r--r--tools/shared.py28
-rw-r--r--tools/test-js-optimizer-output.js5
-rw-r--r--tools/test-js-optimizer.js6
26 files changed, 721 insertions, 223 deletions
diff --git a/AUTHORS b/AUTHORS
index 248c345d..145e3812 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -16,3 +16,4 @@ under the licensing terms detailed in LICENSE.
* Adrian Taylor <adrian@macrobug.com>
* Richard Assar <richard.assar@gmail.com>
* Nathan Hammond <emscripten@nathanhammond.com>
+* Behdad Esfahbod <behdad@behdad.org>
diff --git a/emcc b/emcc
index 044347b7..a6cacb39 100755
--- a/emcc
+++ b/emcc
@@ -74,7 +74,7 @@ emcc can be influenced by a few environment variables:
EMMAKEN_COMPILER - The compiler to be used, if you don't want the default clang.
'''
-import os, sys, shutil, tempfile, subprocess
+import os, sys, shutil, tempfile, subprocess, shlex
from subprocess import PIPE, STDOUT
from tools import shared
@@ -174,6 +174,12 @@ Options that are modified or new in %s include:
2: -O2 LLVM optimizations
3: -O3 LLVM optimizations (default in -O2+)
+ --llvm-lto <level> 0: No LLVM LTO (default in -O0)
+ 1: LLVM LTO (default in -O1+)
+ Note: If LLVM optimizations are not run
+ (see --llvm-opts), setting this to 1 has no
+ effect.
+
--closure <on> 0: No closure compiler (default in -O0, -O1)
1: Run closure compiler (default in -O2, -O3)
@@ -232,6 +238,10 @@ Options that are modified or new in %s include:
receive an array/typed array and return
an array/typed array.
Compression only works when generating HTML.
+ When compression is on, all filed specified
+ to be preloaded are compressed in one big
+ archive, which is given the same name as the
+ output HTML but with suffix .data.compress
--minify <on> 0: Do not minify the generated JavaScript's
whitespace (default if closure compiler
@@ -307,7 +317,7 @@ if os.environ.get('EMMAKEN_CXX'):
CC_ADDITIONAL_ARGS = shared.COMPILER_OPTS # + ['-g']?
EMMAKEN_CFLAGS = os.environ.get('EMMAKEN_CFLAGS')
-if EMMAKEN_CFLAGS: CC_ADDITIONAL_ARGS += EMMAKEN_CFLAGS.split(' ')
+if EMMAKEN_CFLAGS: CC_ADDITIONAL_ARGS += shlex.split(EMMAKEN_CFLAGS)
# ---------------- Utilities ---------------
@@ -319,9 +329,11 @@ ASSEMBLY_SUFFIXES = ('.ll',)
LIB_PREFIXES = ('', 'lib')
IMAGE_SUFFIXES = ('.jpg', '.png', '.bmp')
+AUDIO_SUFFIXES = ('.ogg', '.wav', '.mp3')
+AUDIO_MIMETYPES = { 'ogg': 'audio/ogg', 'wav': 'audio/wav', 'mp3': 'audio/mpeg' }
def suffix(name):
- return name.split('.')[:-1]
+ return name.split('.')[-1]
def unsuffixed(name):
return '.'.join(name.split('.')[:-1])
@@ -347,6 +359,12 @@ for i in range(1, len(sys.argv)):
if arg.endswith('.h') and sys.argv[i-1] != '-include':
header = True
+if '-M' in sys.argv or '-MM' in sys.argv:
+ # Just output dependencies, do not compile. Warning: clang and gcc behave differently with -MF! (clang seems to not recognize it)
+ cmd = [CC] + sys.argv[1:]
+ if DEBUG: print >> sys.stderr, 'emcc, just dependencies: ', ' '.join(cmd)
+ exit(subprocess.call(cmd))
+
# Check if a target is specified
target = None
for i in range(len(sys.argv)-1):
@@ -401,6 +419,7 @@ try:
opt_level = 0
llvm_opts = None
+ llvm_lto = None
closure = None
js_transform = None
pre_js = None
@@ -427,6 +446,11 @@ try:
llvm_opts = eval(newargs[i+1])
newargs[i] = ''
newargs[i+1] = ''
+ elif newargs[i].startswith('--llvm-lto'):
+ check_bad_eq(newargs[i])
+ llvm_lto = eval(newargs[i+1])
+ newargs[i] = ''
+ newargs[i+1] = ''
elif newargs[i].startswith('--closure'):
check_bad_eq(newargs[i])
closure = int(newargs[i+1])
@@ -474,12 +498,6 @@ try:
Compression.on = True
newargs[i] = ''
newargs[i+1] = ''
- elif newargs[i] == '-MF': # clang cannot handle this, so we fake it
- f = open(newargs[i+1], 'w')
- f.write('\n')
- f.close()
- newargs[i] = ''
- newargs[i+1] = ''
elif newargs[i] == '--ignore-dynamic-linking':
ignore_dynamic_linking = True
newargs[i] = ''
@@ -491,6 +509,7 @@ try:
newargs = [ arg for arg in newargs if arg is not '' ]
if llvm_opts is None: llvm_opts = LLVM_OPT_LEVEL[opt_level]
+ if llvm_lto is None: llvm_lto = llvm_opts > 0
if closure is None: closure = 1 if opt_level >= 2 else 0
if minify_whitespace is None:
minify_whitespace = closure # if closure is run, minify whitespace
@@ -776,7 +795,7 @@ try:
shared.Building.llvm_opt(in_temp(target_basename + '.bc'), llvm_opts)
if DEBUG: save_intermediate('opt', 'bc')
# Do LTO in a separate pass to work around LLVM bug XXX (see failure e.g. in cubescript)
- if shared.Building.can_use_unsafe_opts() and shared.Building.can_build_standalone():
+ if llvm_lto and shared.Building.can_use_unsafe_opts() and shared.Building.can_build_standalone():
lto_opts = []
if not shared.Building.can_inline(): lto_opts.append('-disable-inlining')
lto_opts.append('-std-link-opts')
@@ -813,6 +832,23 @@ try:
if DEBUG: print >> sys.stderr, 'emcc: setting up files'
code = ''
+ if final_suffix == 'html':
+ code += '''
+ var BlobBuilder = typeof MozBlobBuilder != "undefined" ? MozBlobBuilder : (typeof WebKitBlobBuilder != "undefined" ? WebKitBlobBuilder : console.log("warning: cannot build blobs"));
+ var URLObject = typeof window != "undefined" ? (window.URL ? window.URL : window.webkitURL) : console.log("warning: cannot create object URLs");
+ var hasBlobConstructor;
+ try {
+ new Blob();
+ hasBlobConstructor = true;
+ } catch(e) {
+ hasBlobConstructor = false;
+ console.log("warning: no blob constructor, cannot create blobs with mimetypes");
+ }
+'''
+
+ code += 'var preloadedImages = {}; // maps url to image data\n'
+ code += 'var preloadedAudios = {}; // maps url to audio data\n'
+
# Expand directories into individual files
def add(mode, dirname, names):
for name in names:
@@ -827,19 +863,8 @@ try:
for file_ in data_files:
file_['net_name'] = file_['name']
- file_['compressed'] = False
- if Compression.on:
- # Compress each file, if it is worth it
- for file_ in data_files:
- Compression.compress(file_['name'])
- if Compression.worth_it(os.stat(file_['name']).st_size,
- os.stat(Compression.compressed_name(file_['name'])).st_size):
- file_['net_name'] = Compression.compressed_name(file_['name'])
- file_['compressed'] = True
- else:
- if DEBUG: print >> sys.stderr, 'emcc: not compressing %s since not worth it' % file_['name']
- os.remove(Compression.compressed_name(file_['name']))
+ data_target = unsuffixed(target) + '.data'
# Set up folders
partial_dirs = []
@@ -853,8 +878,31 @@ try:
code += '''FS.createFolder('/%s', '%s', true, false);\n''' % (os.path.sep.join(parts[:i]), parts[i])
partial_dirs.append(partial)
- code += 'var BlobBuilder = typeof MozBlobBuilder != "undefined" ? MozBlobBuilder : (typeof WebKitBlobBuilder != "undefined" ? WebKitBlobBuilder : console.log("warning: cannot build blobs"));\n'
- code += 'var URLObject = typeof window != "undefined" ? (window.URL ? window.URL : window.webkitURL) : console.log("warning: cannot create object URLs");\n'
+ if final_suffix == 'html':
+ # Bundle all datafiles into one archive. Avoids doing lots of simultaneous XHRs which has overhead.
+ data = open(data_target, 'wb')
+ start = 0
+ for file_ in data_files:
+ file_['data_start'] = start
+ curr = open(file_['name']).read()
+ file_['data_end'] = start + len(curr)
+ start += len(curr)
+ data.write(curr)
+ data.close()
+ if Compression.on:
+ Compression.compress(data_target)
+
+ # Data requests - for getting a block of data out of the big archive - have a similar API to XHRs
+ code += '''
+ function DataRequest() {}
+ DataRequest.prototype = {
+ requests: {},
+ open: function(mode, name) {
+ this.requests[name] = this;
+ },
+ send: function() {}
+ };
+ '''
counter = 0
for file_ in data_files:
@@ -864,56 +912,121 @@ try:
code += '''FS.createDataFile('/', '%s', %s, true, true);\n''' % (os.path.basename(filename), str(map(ord, open(filename, 'rb').read())))
elif file_['mode'] == 'preload':
# Preload
+ assert final_suffix == 'html', 'Can only preload files when generating HTML'
+
varname = 'filePreload%d' % counter
counter += 1
image = filename.endswith(IMAGE_SUFFIXES)
+ audio = filename.endswith(AUDIO_SUFFIXES)
+
+ if image:
+ finish = '''
+ var bb = new BlobBuilder();
+ bb.append(byteArray.buffer);
+ var b = bb.getBlob();
+ var url = URLObject.createObjectURL(b);
+ var img = new Image();
+ img.onload = function() {
+ assert(img.complete, 'Image %(filename)s could not be decoded');
+ var canvas = document.createElement('canvas');
+ canvas.width = img.width;
+ canvas.height = img.height;
+ var ctx = canvas.getContext('2d');
+ ctx.drawImage(img, 0, 0);
+ preloadedImages['%(filename)s'] = canvas;
+ URLObject.revokeObjectURL(url);
+ removeRunDependency();
+ };
+ img.onerror = function(event) {
+ console.log('Image %(filename)s could not be decoded');
+ };
+ img.src = url;
+''' % { 'filename': filename }
+ elif audio:
+ # Need actual blob constructor here, to set the mimetype or else audios fail to decode
+ finish = '''
+ if (hasBlobConstructor) {
+ var b = new Blob([byteArray.buffer], { type: '%(mimetype)s' });
+ var url = URLObject.createObjectURL(b); // XXX we never revoke this!
+ var audio = new Audio();
+ audio['oncanplaythrough'] = function() { // XXX string for closure
+ audio['oncanplaythrough'] = null;
+ preloadedAudios['%(filename)s'] = audio;
+ removeRunDependency();
+ };
+ audio.onerror = function(event) {
+ console.log('Audio %(filename)s could not be decoded');
+ };
+ audio.src = url;
+ } else {
+ preloadedAudios['%(filename)s'] = new Audio(); // empty shim
+ removeRunDependency();
+ }
+''' % { 'filename': filename, 'mimetype': AUDIO_MIMETYPES[suffix(filename)] }
+ else:
+ finish = 'removeRunDependency();\n'
+
code += '''
- var %(varname)s = new XMLHttpRequest();
+ var %(varname)s = new %(request)s();
%(varname)s.open('GET', '%(netname)s', true);
%(varname)s.responseType = 'arraybuffer';
%(varname)s.onload = function() {
- var arrayBuffer = %(varname)s.response; // Note: not X.responseText
+ var arrayBuffer = %(varname)s.response;
assert(arrayBuffer, 'Loading file %(filename)s failed.');
- var byteArray = new Uint8Array(arrayBuffer);
- %(decompress_start)s
+ var byteArray = arrayBuffer.byteLength ? new Uint8Array(arrayBuffer) : arrayBuffer;
FS.createDataFile('/%(dirname)s', '%(basename)s', byteArray, true, true);
%(finish)s
- %(decompress_end)s
};
addRunDependency();
%(varname)s.send(null);
''' % {
+ 'request': 'DataRequest', # In the past we also supported XHRs here
'varname': varname,
'filename': filename,
'netname': file_['net_name'],
'dirname': os.path.dirname(filename),
'basename': os.path.basename(filename),
- 'decompress_start': '' if not file_['compressed'] else 'Module["decompress"](byteArray, function(decompressed) { byteArray = new Uint8Array(decompressed);',
- 'decompress_end': '' if not file_['compressed'] else '});',
- 'finish': 'removeRunDependency();' if not image else '''var bb = new BlobBuilder();
- bb.append(byteArray.buffer);
- var b = bb.getBlob();
- var url = URLObject.createObjectURL(b);
- var img = new Image();
- img.onload = function() {
- assert(img.complete, 'Image %(filename)s could not be decoded');
- var canvas = document.createElement('canvas');
- canvas.width = img.width;
- canvas.height = img.height;
- var ctx = canvas.getContext('2d');
- ctx.drawImage(img, 0, 0);
- preloadedImages['%(filename)s'] = canvas;
- URLObject.revokeObjectURL(url);
- removeRunDependency();
- };
- img.onerror = function(event) {
- console.log('Image %(filename)s could not be decoded: ' + JSON.stringify(event));
- };
- img.src = url;
-''' % { 'filename': filename }
+ 'finish': finish
}
else:
assert 0
+
+ if final_suffix == 'html':
+ # Get the big archive and split it up
+ use_data = ''
+ for file_ in data_files:
+ if file_['mode'] == 'preload':
+ use_data += '''
+ curr = DataRequest.prototype.requests['%s'];
+ curr.response = byteArray.subarray(%d,%d);
+ curr.onload();
+ ''' % (file_['name'], file_['data_start'], file_['data_end'])
+ use_data += ' removeRunDependency();\n'
+
+ if Compression.on:
+ use_data = '''
+ Module["decompress"](byteArray, function(decompressed) {
+ byteArray = new Uint8Array(decompressed);
+ %s
+ });
+ ''' % use_data
+
+ code += '''
+ var dataFile = new XMLHttpRequest();
+ dataFile.open('GET', '%s', true);
+ dataFile.responseType = 'arraybuffer';
+ dataFile.onload = function() {
+ var arrayBuffer = dataFile.response;
+ assert(arrayBuffer, 'Loading data file failed.');
+ var byteArray = new Uint8Array(arrayBuffer);
+ var curr;
+ %s
+ };
+ addRunDependency();
+ dataFile.send(null);
+ if (Module['setStatus']) Module['setStatus']('Downloading...');
+ ''' % (Compression.compressed_name(data_target) if Compression.on else data_target, use_data)
+
src = open(final).read().replace('// {{PRE_RUN_ADDITIONS}}', code)
final += '.files.js'
open(final, 'w').write(src)
@@ -932,7 +1045,7 @@ try:
shutil.copyfile(final, final + '.tr.js')
final += '.tr.js'
if DEBUG: print >> sys.stderr, 'emcc: applying transform: %s' % js_transform
- execute(js_transform.split(' ') + [os.path.abspath(final)])
+ execute(shlex.split(js_transform) + [os.path.abspath(final)])
if DEBUG: save_intermediate('transformed')
# It is useful to run several js optimizer passes together, to save on unneeded unparsing/reparsing
@@ -1013,14 +1126,25 @@ try:
decoding = '''
var decompressWorker = new Worker('decompress.js');
var decompressCallbacks = [];
+ var decompressions = 0;
Module["decompress"] = function(data, callback) {
var id = decompressCallbacks.length;
decompressCallbacks.push(callback);
decompressWorker.postMessage({ data: data, id: id });
+ if (Module['setStatus']) {
+ decompressions++;
+ Module['setStatus']('Decompressing...');
+ }
};
decompressWorker.onmessage = function(event) {
decompressCallbacks[event.data.id](event.data.data);
decompressCallbacks[event.data.id] = null;
+ if (Module['setStatus']) {
+ decompressions--;
+ if (decompressions == 0) {
+ Module['setStatus']('');
+ }
+ }
};
var compiledCodeXHR = new XMLHttpRequest();
compiledCodeXHR.open('GET', '%s', true);
diff --git a/src/library.js b/src/library.js
index aa1fd242..78dd629b 100644
--- a/src/library.js
+++ b/src/library.js
@@ -445,6 +445,14 @@ LibraryManager.library = {
standardizePath: function(path) {
if (path.substr(0, 2) == './') path = path.substr(2);
return path;
+ },
+
+ deleteFile: function(path) {
+ var path = FS.analyzePath(path);
+ if (!path.parentExists || !path.exists) {
+ throw 'Invalid path ' + path;
+ }
+ delete path.parentObject.contents[path.name];
}
},
@@ -4642,16 +4650,20 @@ LibraryManager.library = {
_ZTIPv: [0],
llvm_uadd_with_overflow_i32: function(x, y) {
+ x = x>>>0;
+ y = y>>>0;
return {
- f0: x+y,
- f1: 0 // We never overflow... for now
+ f0: (x+y)>>>0,
+ f1: x+y > 4294967295
};
},
llvm_umul_with_overflow_i32: function(x, y) {
+ x = x>>>0;
+ y = y>>>0;
return {
- f0: x*y,
- f1: 0 // We never overflow... for now
+ f0: (x*y)>>>0,
+ f1: x*y > 4294967295
};
},
diff --git a/src/library_gl.js b/src/library_gl.js
index 6b0a270f..f6fb2e27 100644
--- a/src/library_gl.js
+++ b/src/library_gl.js
@@ -1,6 +1,11 @@
//"use strict";
-// XXX FIXME Hardcoded '4' in many places, here and in library_SDL, for RGBA
+// FIXME:
+// * glGetUniformLocation should return -1 when the value is not valid, not null
+// * glUniform1fi should be glUniform1iv
+// * glGetAttribLocation lacks return value (and should be -1 when not valid)
+// * single-underscore deps need double underscore (and, just auto-add them all)
+// * glGetProgramInfoLog and *shader* should be essentially identical
var LibraryGL = {
$GL: {
diff --git a/src/library_sdl.js b/src/library_sdl.js
index 06e10339..631de481 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -175,17 +175,21 @@ mergeInto(LibraryManager.library, {
},
// Load SDL color into a CSS-style color specification
- loadColorToCSS: function(color) {
+ loadColorToCSSRGB: function(color) {
var rgba = {{{ makeGetValue('color', '0', 'i32') }}};
- return 'rgba(' + (rgba&255) + ',' + ((rgba >> 8)&255) + ',' + ((rgba >> 16)&255) + ',' + (1-((rgba >> 24)&255)/255) + ')';
+ return 'rgb(' + (rgba&255) + ',' + ((rgba >> 8)&255) + ',' + ((rgba >> 16)&255) + ')';
+ },
+ loadColorToCSSRGBA: function(color) {
+ var rgba = {{{ makeGetValue('color', '0', 'i32') }}};
+ return 'rgba(' + (rgba&255) + ',' + ((rgba >> 8)&255) + ',' + ((rgba >> 16)&255) + ',' + (((rgba >> 24)&255)/255) + ')';
},
- translateColorToCSS: function(rgba) {
- return 'rgba(' + ((rgba >> 24)&255) + ',' + ((rgba >> 16)&255) + ',' + ((rgba >> 8)&255) + ',' + (1-(rgba&255)/255) + ')';
+ translateColorToCSSRGBA: function(rgba) {
+ return 'rgba(' + ((rgba >> 24)&255) + ',' + ((rgba >> 16)&255) + ',' + ((rgba >> 8)&255) + ',' + ((rgba&255)/255) + ')';
},
- translateRGBAToCSS: function(r, g, b, a) {
- return 'rgba(' + r + ',' + g + ',' + b + ',' + (1-a/255) + ')';
+ translateRGBAToCSSRGBA: function(r, g, b, a) {
+ return 'rgba(' + r + ',' + g + ',' + b + ',' + (a/255) + ')';
},
makeSurface: function(width, height, flags, usePageCanvas, source) {
@@ -273,6 +277,11 @@ mergeInto(LibraryManager.library, {
switch(event.type) {
case 'keydown': case 'keyup': case 'mousedown': case 'mouseup': case 'mousemove':
SDL.events.push(event);
+ if ((event.keyCode >= 37 && event.keyCode <= 40) || // arrow keys
+ event.keyCode == 32 || // space
+ event.keyCode == 33 || event.keyCode == 34) { // page up/down
+ event.preventDefault();
+ }
break;
}
//event.preventDefault();
@@ -342,7 +351,7 @@ mergeInto(LibraryManager.library, {
var tempCtx = SDL.surfaces[SDL.screen].ctx;
tempCtx.save();
tempCtx.font = fontString;
- var ret = tempCtx.measureText(text).width;
+ var ret = tempCtx.measureText(text).width | 0;
tempCtx.restore();
return ret;
},
@@ -408,6 +417,9 @@ mergeInto(LibraryManager.library, {
},
SDL_Quit: function() {
+ for (var i = 0; i < SDL.audios; i++) {
+ SDL.audios[i].pause();
+ }
Module.print('SDL_Quit called (and ignored)');
},
@@ -459,13 +471,14 @@ mergeInto(LibraryManager.library, {
assert(buffer % 4 == 0, 'Invalid buffer offset: ' + buffer);
var src = buffer >> 2;
var dst = 0;
+ var isScreen = surf == SDL.screen;
while (dst < num) {
// TODO: access underlying data buffer and write in 32-bit chunks or more
var val = HEAP32[src]; // This is optimized. Instead, we could do {{{ makeGetValue('buffer', 'dst', 'i32') }}};
- data[dst] = val & 0xff;
+ data[dst ] = val & 0xff;
data[dst+1] = (val >> 8) & 0xff;
data[dst+2] = (val >> 16) & 0xff;
- data[dst+3] = (val >> 24) & 0xff;
+ data[dst+3] = isScreen ? 0xff : ((val >> 24) & 0xff);
src++;
dst += 4;
}
@@ -582,14 +595,14 @@ mergeInto(LibraryManager.library, {
assert(!surfData.locked); // but we could unlock and re-lock if we must..
var r = SDL.loadRect(rect);
surfData.ctx.save();
- surfData.ctx.fillStyle = SDL.translateColorToCSS(color);
+ surfData.ctx.fillStyle = SDL.translateColorToCSSRGBA(color);
surfData.ctx.fillRect(r.x, r.y, r.w, r.h);
surfData.ctx.restore();
},
SDL_BlitSurface__deps: ['SDL_UpperBlit'],
SDL_BlitSurface: function(src, srcrect, dst, dstrect) {
- return _SDL_Blit(src, srcrect, dst, dstrect);
+ return _SDL_UpperBlit(src, srcrect, dst, dstrect);
},
SDL_SetAlpha: function(surf, flag, alpha) {
@@ -716,15 +729,14 @@ mergeInto(LibraryManager.library, {
SDL_CondWait: function() {},
SDL_DestroyCond: function() {},
-//SDL_CreateYUVOverlay
-//SDL_CreateThread, SDL_WaitThread etc
-
// SDL Mixer
- Mix_OpenAudio: function() { return 0 },
+ Mix_OpenAudio: function(frequency, format, channels, chunksize) {
+ return 0;
+ },
Mix_HookMusicFinished: function(func) {
- SDL.hookMusicFinished = func; // TODO: use this
+ SDL.hookMusicFinished = func;
},
Mix_VolumeMusic: function(func) {
@@ -733,46 +745,39 @@ mergeInto(LibraryManager.library, {
Mix_LoadWAV_RW: function(filename, freesrc) {
filename = FS.standardizePath(Pointer_stringify(filename));
+ var raw = preloadedAudios[filename];
+ assert(raw, 'Cannot find preloaded audio ' + filename);
var id = SDL.audios.length;
SDL.audios.push({
- audio: new Audio(filename)
+ source: filename,
+ audio: raw
});
return id;
},
- Mix_FreeChunk: function(audio) {
+ Mix_FreeChunk: function(id) {
SDL.audios[id].audio.pause();
SDL.audios[id] = null;
- return 0;
},
- Mix_PlayChannel: function(channel, audio, loops) {
+ Mix_PlayChannel: function(channel, id, loops) {
+ // TODO: handle loops
var audio = SDL.audios[id].audio;
+ if (audio.currentTime) audio.src = audio.src; // This hack prevents lags on replaying // TODO: parallel sounds through //cloneNode(true).play()
audio.play();
- return 0; // XXX should return channel
+ return 1; // XXX should return channel
},
Mix_PlayChannelTimed: 'Mix_PlayChannel', // XXX ignore Timing
- Mix_LoadMUS: function(filename) {
- filename = FS.standardizePath(Pointer_stringify(filename));
- var id = SDL.audios.length;
- SDL.audios.push({
- audio: new Audio(filename)
- });
- return id;
- },
-
- Mix_FreeMusic: function(id) {
- SDL.audios[id].audio.pause();
- SDL.audios[id] = null;
- return 0;
- },
+ Mix_LoadMUS: 'Mix_LoadWAV_RW',
+ Mix_FreeMusic: 'Mix_FreeChunk',
Mix_PlayMusic: function(id, loops) {
- if (loops == 0) return;
+ loops = Math.max(loops, 1);
var audio = SDL.audios[id].audio;
- audio.loop = loop != 1; // TODO: handle N loops for finite N
+ audio.loop = loops != 1; // TODO: handle N loops for finite N
audio.play();
+ SDL.music = audio;
return 0;
},
@@ -788,15 +793,21 @@ mergeInto(LibraryManager.library, {
return 0;
},
- Mix_HaltMusic: function(id) {
- var audio = SDL.audios[id].audio;
- audio.pause(); // TODO: actually rewind to the beginning
+ Mix_HaltMusic: function() {
+ var audio = SDL.music;
+ if (!audio) return 0;
+ audio.src = audio.src; // rewind
+ audio.pause();
+ SDL.music = null;
+ if (SDL.hookMusicFinished) {
+ FUNCTION_TABLE[SDL.hookMusicFinished]();
+ }
return 0;
},
Mix_FadeInMusicPos: 'Mix_PlayMusic', // XXX ignore fading in effect
- Mix_FadeOutMusic: function(id) {}, // TODO
+ Mix_FadeOutMusic: 'Mix_HaltMusic', // XXX ignore fading out effect
// SDL TTF
@@ -818,7 +829,7 @@ mergeInto(LibraryManager.library, {
var fontData = SDL.fonts[font];
var w = SDL.estimateTextWidth(fontData, text);
var h = fontData.size;
- var color = SDL.loadColorToCSS(color);
+ var color = SDL.loadColorToCSSRGB(color); // XXX alpha breaks fonts?
var fontString = h + 'px sans-serif';
var surf = SDL.makeSurface(w, h, 0, false, 'text:' + text); // bogus numbers..
var surfData = SDL.surfaces[surf];
@@ -861,7 +872,7 @@ mergeInto(LibraryManager.library, {
assert(!surfData.locked); // but we could unlock and re-lock if we must..
// TODO: if ctx does not change, leave as is, and also do not re-set xStyle etc.
surfData.ctx.save();
- surfData.ctx.fillStyle = SDL.translateRGBAToCSS(r, g, b, a);
+ surfData.ctx.fillStyle = SDL.translateRGBAToCSSRGBA(r, g, b, a);
surfData.ctx.fillRect(x1, y1, x2-x1, y2-y1);
surfData.ctx.restore();
},
@@ -870,7 +881,7 @@ mergeInto(LibraryManager.library, {
var surfData = SDL.surfaces[surf];
assert(!surfData.locked); // but we could unlock and re-lock if we must..
surfData.ctx.save();
- surfData.ctx.strokeStyle = SDL.translateRGBAToCSS(r, g, b, a);
+ surfData.ctx.strokeStyle = SDL.translateRGBAToCSSRGBA(r, g, b, a);
surfData.ctx.strokeRect(x1, y1, x2-x1, y2-y1);
surfData.ctx.restore();
},
@@ -879,7 +890,7 @@ mergeInto(LibraryManager.library, {
var surfData = SDL.surfaces[surf];
assert(!surfData.locked); // but we could unlock and re-lock if we must..
surfData.ctx.save();
- surfData.ctx.strokeStyle = SDL.translateRGBAToCSS(r, g, b, a);
+ surfData.ctx.strokeStyle = SDL.translateRGBAToCSSRGBA(r, g, b, a);
surfData.ctx.beginPath();
surfData.ctx.moveTo(x1, y1);
surfData.ctx.lineTo(x2, y2);
diff --git a/src/parseTools.js b/src/parseTools.js
index 520d278e..0e6ddee8 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -1537,7 +1537,8 @@ function makeRounding(value, bits, signed, floatConversion) {
// TODO: handle roundings of i64s
assert(bits);
// C rounds to 0 (-5.5 to -5, +5.5 to 5), while JS has no direct way to do that.
- if (bits <= 32 && signed) return '((' + value + ')|0)'; // This is fast and even correct, for all cases
+ if (bits <= 32 && signed) return '((' + value + ')&-1)'; // This is fast and even correct, for all cases. Note that it is the same
+ // 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 + ')';
diff --git a/src/postamble.js b/src/postamble.js
index 62966d61..b14a31a1 100644
--- a/src/postamble.js
+++ b/src/postamble.js
@@ -32,6 +32,10 @@ Module.callMain = function callMain(args) {
function run(args) {
args = args || Module['arguments'];
+ if (Module['setStatus']) {
+ Module['setStatus'](''); // clear the status from "Downloading.." etc.
+ }
+
if (Module['preRun']) {
Module['preRun']();
}
diff --git a/src/preamble.js b/src/preamble.js
index 86a5ce80..c88a4671 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -850,9 +850,5 @@ function removeRunDependency() {
if (runDependencies == 0) run();
}
-// Preloading
-
-var preloadedImages = {}; // maps url to image data
-
// === Body ===
diff --git a/src/shell.html b/src/shell.html
index 79b7e9b9..436ba37e 100644
--- a/src/shell.html
+++ b/src/shell.html
@@ -4,34 +4,36 @@
<body>
<center>
<canvas id='canvas' width='256' height='256'></canvas>
+ <hr>
+ <textarea id="output" style="font-family: monospace; width: 80%" rows="8"></textarea>
+ <hr>
+ <div id='status'>Downloading...</div>
</center>
<hr>
- <div id='output'></div>
- <hr>
- <center><div id='status'></div></center>
- <hr>
<script type='text/javascript'>
// connect to canvas
var Module = {
print: (function() {
var element = document.getElementById('output');
- var printBuffer = [];
+ element.value = ''; // clear browser cache
return function(text) {
- text = text.replace(/&/g, "&amp;");
- text = text.replace(/</g, "&lt;");
- text = text.replace(/>/g, "&gt;");
- text = text.replace('\n', '<br>', 'g');
- if (printBuffer.length > 10) printBuffer.shift();
- printBuffer.push(text);
- element.innerHTML = printBuffer.join('<br>');
+ // These replacements are necessary if you render to raw HTML
+ //text = text.replace(/&/g, "&amp;");
+ //text = text.replace(/</g, "&lt;");
+ //text = text.replace(/>/g, "&gt;");
+ //text = text.replace('\n', '<br>', 'g');
+ element.value += text + "\n";
+ element.scrollTop = 99999; // focus on bottom
};
})(),
canvas: document.getElementById('canvas'),
+ setStatus: function(text) {
+ document.getElementById('status').innerHTML = text;
+ },
totalDependencies: 0,
monitorRunDependencies: function(left) {
this.totalDependencies = Math.max(this.totalDependencies, left);
- document.getElementById('status').innerHTML = left ? 'Downloading files: ' + (this.totalDependencies-left) + '/' + this.totalDependencies :
- 'All downloads complete.';
+ Module.setStatus(left ? 'Downloading: ' + (this.totalDependencies-left) + '/' + this.totalDependencies : 'All downloads complete.');
}
};
diff --git a/system/include/libc/stdio.h b/system/include/libc/stdio.h
index 19e460a3..4e14df79 100644
--- a/system/include/libc/stdio.h
+++ b/system/include/libc/stdio.h
@@ -142,6 +142,7 @@ typedef _fpos64_t fpos64_t;
#define TMP_MAX 26
+#if 0 /* XXX Emscripten: do not use impure stuff for std*, it makes comparing to native builds harder */
#ifndef _REENT_ONLY
#define stdin (_REENT->_stdin)
#define stdout (_REENT->_stdout)
@@ -151,6 +152,14 @@ typedef _fpos64_t fpos64_t;
#define stdout (_impure_ptr->_stdout)
#define stderr (_impure_ptr->_stderr)
#endif /* _REENT_ONLY */
+#else
+extern FILE *stdin;
+extern FILE *stdout;
+extern FILE *stderr;
+#define stdin stdin
+#define stdout stdout
+#define stderr stderr
+#endif
#define _stdin_r(x) ((x)->_stdin)
#define _stdout_r(x) ((x)->_stdout)
diff --git a/tests/cases/uadd_overflow.ll b/tests/cases/uadd_overflow.ll
new file mode 100644
index 00000000..a808b9de
--- /dev/null
+++ b/tests/cases/uadd_overflow.ll
@@ -0,0 +1,25 @@
+; 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"
+
+@.str2 = private constant [9 x i8] c"*%d,%d*\0A\00", align 1 ; [#uses=1]
+
+; [#uses=0]
+define i32 @main() {
+entry:
+ %retval = alloca i32, align 4 ; [#uses=1 type=i32*]
+ %mul7 = bitcast i32 -259741926 to i32
+ %shl10 = shl i32 4014, 16
+ %uadd1 = tail call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 %mul7, i32 %shl10)
+ %a0 = extractvalue { i32, i1 } %uadd1, 0
+ %a1 = extractvalue { i32, i1 } %uadd1, 1
+ %a2 = zext i1 %a1 to i32
+ call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([9 x i8]* @.str2, i32 0, i32 0), i32 %a0, i32 %a2) ; [#uses=0]
+ ret i32 1
+}
+
+; [#uses=1]
+declare i32 @printf(i8*, ...)
+
+declare { i32, i1 } @llvm.uadd.with.overflow.i32(i32, i32) nounwind readnone
+
diff --git a/tests/cases/uadd_overflow.txt b/tests/cases/uadd_overflow.txt
new file mode 100644
index 00000000..dcda9240
--- /dev/null
+++ b/tests/cases/uadd_overflow.txt
@@ -0,0 +1 @@
+*3319578,1*
diff --git a/tests/freetype/main_2.c b/tests/freetype/main_2.c
new file mode 100644
index 00000000..85e5609d
--- /dev/null
+++ b/tests/freetype/main_2.c
@@ -0,0 +1,135 @@
+/* example1.c */
+/* */
+/* This small program shows how to print a rotated string with the */
+/* FreeType 2 library. */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <stdlib.h>
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+int WIDTH = 0;
+int HEIGHT = 0;
+
+/* origin is the upper left corner */
+unsigned char *image;
+
+
+/* Replace this function with something useful. */
+
+void
+draw_bitmap( FT_Bitmap* bitmap,
+ FT_Int x,
+ FT_Int y)
+{
+ FT_Int i, j, p, q;
+ FT_Int x_max = x + bitmap->width;
+ FT_Int y_max = y + bitmap->rows;
+
+ for ( i = x, p = 0; i < x_max; i++, p++ )
+ {
+ for ( j = y, q = 0; j < y_max; j++, q++ )
+ {
+ if ( i < 0 || j < 0 ||
+ i >= WIDTH || j >= HEIGHT )
+ continue;
+
+ image[j*WIDTH + i] |= bitmap->buffer[q * bitmap->width + p];
+ }
+ }
+}
+
+
+void
+show_image( void )
+{
+ int i, j;
+ int count = 0;
+
+ for ( i = 0; i < HEIGHT; i++ )
+ {
+ for ( j = 0; j < WIDTH; j++ )
+ {
+ if (image[i*WIDTH + j]) count++;
+ putchar(image[i*WIDTH + j] == 0? ' '
+ : image[i*WIDTH + j] < 128 ? '+'
+ : '*' );
+ }
+ putchar( '\n' );
+ }
+ printf("Non-0s: %d\n", count);
+}
+
+
+int
+main( int argc,
+ char** argv )
+{
+ FT_Library library;
+ FT_Face face;
+
+ FT_GlyphSlot slot;
+ FT_Error error;
+
+ FT_UInt glyphIndex;
+
+ char* filename;
+ char* text;
+
+ int target_height;
+
+ if ( argc != 6 )
+ {
+ fprintf ( stderr, "usage: %s font sample-text width height angle\n", argv[0] );
+ exit( 1 );
+ }
+
+ // Only test the character 'w'
+ text = "w";
+
+ filename = argv[1]; /* first argument */
+ WIDTH = atoi(argv[3]);
+ HEIGHT = atoi(argv[4]);
+ target_height = HEIGHT;
+
+ image = (unsigned char*)malloc(WIDTH*HEIGHT);
+ for (int x = 0; x < WIDTH; x++)
+ for (int y = 0; y < HEIGHT; y++)
+ image[y*WIDTH + x] = 0;
+
+ error = FT_Init_FreeType( &library ); /* initialize library */
+ if (error) printf("Init Error! %d\n", error);
+
+ error = FT_New_Face( library, argv[1], 0, &face ); /* create face object */
+ if (error) printf("New_Face Error! %d\n", error);
+
+ /* use 50pt at 100dpi */
+ error = FT_Set_Char_Size( face, 0, 32 * 64, 0, 0 ); /* set character size */
+ if (error) printf("Set_Cshar_Size Error! %d\n", error);
+
+ slot = face->glyph;
+
+ glyphIndex = FT_Get_Char_Index(face, text[0]);
+
+ /* load glyph image into the slot (erase previous one) */
+ error = FT_Load_Glyph(face, glyphIndex, FT_LOAD_NO_BITMAP);
+ if(error) printf("FT_Load_Glyph Error! %d\n", error);
+
+ error = FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL);
+ if(error) printf("FT_Render_Glyph Error! %d\n", error);
+
+ /* now, draw to our target surface (convert position) */
+ draw_bitmap(&slot->bitmap, slot->bitmap_left, target_height - slot->bitmap_top);
+
+ show_image();
+
+ FT_Done_Face(face);
+ FT_Done_FreeType(library);
+ return 0;
+}
+
+/* EOF */ \ No newline at end of file
diff --git a/tests/freetype/ref_2.txt b/tests/freetype/ref_2.txt
new file mode 100644
index 00000000..c15bb415
--- /dev/null
+++ b/tests/freetype/ref_2.txt
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+****+ +***** +****
+****+ +*****+ +****
++**** ******+ ****+
++**** ******* ****+
++****+ +***+*** +****
+ ****+ +***+***+ +***+
+ +***+ **** ***+ ****+
+ +**** ***+ +*** ****+
+ +**** +***+ +*** +****
+ ****++*** +***++***+
+ +***+**** ***++***+
+ +***+***+ +*******+
+ *******+ +*******
+ ******* +******+
+ +*****+ ******+
+ +*****+ +*****
+ *****+ +*****
+Non-0s: 285
diff --git a/tests/hello_world_sdl.cpp b/tests/hello_world_sdl.cpp
index 35aec303..b6401995 100644
--- a/tests/hello_world_sdl.cpp
+++ b/tests/hello_world_sdl.cpp
@@ -14,13 +14,13 @@ int main() {
*((char*)screen->pixels + i*256*4 + j*4 + 0) = i;
*((char*)screen->pixels + i*256*4 + j*4 + 1) = j;
*((char*)screen->pixels + i*256*4 + j*4 + 2) = 255-i;
- *((char*)screen->pixels + i*256*4 + j*4 + 3) = 255;
+ *((char*)screen->pixels + i*256*4 + j*4 + 3) = (i+j)%255; // actually ignored, since this is to the screen
}
}
if (SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen);
SDL_Flip(screen);
- printf("you should see a colored cube.\n");
+ printf("you should see a smoothly-colored square - no sharp lines but the square borders!\n");
printf("and here is some text that should be HTML-friendly: amp: |&| double-quote: |\"| quote: |'| less-than, greater-than, html-like tags: |<cheez></cheez>|\nanother line.\n");
SDL_Quit();
diff --git a/tests/runner.py b/tests/runner.py
index 6d060f7e..27142eaf 100755
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -1882,75 +1882,81 @@ m_divisor is 1091269979
'''
self.do_run(src, 'z:1*', force_c=True)
- if self.emcc_args is not None: # too slow in other modes
- # We should not blow up the stack with numerous allocas
+ def test_alloca_stack(self):
+ if self.emcc_args is None: return # too slow in other modes
- src = '''
- #include <stdio.h>
- #include <stdlib.h>
+ # We should not blow up the stack with numerous allocas
+ src = '''
+ #include <stdio.h>
+ #include <stdlib.h>
- func(int i) {
- char *pc = (char *)alloca(100);
- *pc = i;
- (*pc)++;
- return (*pc) % 10;
- }
- int main() {
- int total = 0;
- for (int i = 0; i < 1024*1024; i++)
- total += func(i);
- printf("ok:%d*\\n", total);
- return 0;
- }
- '''
- self.do_run(src, 'ok:-32768*', force_c=True)
+ func(int i) {
+ char *pc = (char *)alloca(100);
+ *pc = i;
+ (*pc)++;
+ return (*pc) % 10;
+ }
+ int main() {
+ int total = 0;
+ for (int i = 0; i < 1024*1024; i++)
+ total += func(i);
+ printf("ok:%d*\\n", total);
+ return 0;
+ }
+ '''
+ self.do_run(src, 'ok:-32768*', force_c=True)
- # We should also not blow up the stack with byval arguments
- src = r'''
- #include<stdio.h>
- struct vec {
- int x, y, z;
- vec(int x_, int y_, int z_) : x(x_), y(y_), z(z_) {}
- static vec add(vec a, vec b) {
- return vec(a.x+b.x, a.y+b.y, a.z+b.z);
- }
- };
- int main() {
- int total = 0;
- for (int i = 0; i < 1000; i++) {
- for (int j = 0; j < 1000; j++) {
- vec c(i+i%10, j*2, i%255);
- vec d(j*2, j%255, i%120);
- vec f = vec::add(c, d);
- total += (f.x + f.y + f.z) % 100;
- total %= 10240;
- }
+ def test_stack_byval(self):
+ if self.emcc_args is None: return # too slow in other modes
+
+ # We should also not blow up the stack with byval arguments
+ src = r'''
+ #include<stdio.h>
+ struct vec {
+ int x, y, z;
+ vec(int x_, int y_, int z_) : x(x_), y(y_), z(z_) {}
+ static vec add(vec a, vec b) {
+ return vec(a.x+b.x, a.y+b.y, a.z+b.z);
+ }
+ };
+ int main() {
+ int total = 0;
+ for (int i = 0; i < 1000; i++) {
+ for (int j = 0; j < 1000; j++) {
+ vec c(i+i%10, j*2, i%255);
+ vec d(j*2, j%255, i%120);
+ vec f = vec::add(c, d);
+ total += (f.x + f.y + f.z) % 100;
+ total %= 10240;
}
- printf("sum:%d*\n", total);
- return 1;
}
- '''
- self.do_run(src, 'sum:9780*')
+ printf("sum:%d*\n", total);
+ return 1;
+ }
+ '''
+ self.do_run(src, 'sum:9780*')
- # We should not blow up the stack with numerous varargs
+ def test_stack_varargs(self):
+ if self.emcc_args is None: return # too slow in other modes
- src = r'''
- #include <stdio.h>
- #include <stdlib.h>
+ # We should not blow up the stack with numerous varargs
+ src = r'''
+ #include <stdio.h>
+ #include <stdlib.h>
- void func(int i) {
- printf("%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
- i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i);
- }
- int main() {
- for (int i = 0; i < 1024; i++)
- func(i);
- printf("ok!\n");
- return 0;
- }
- '''
- Settings.TOTAL_STACK = 1024
- self.do_run(src, 'ok!')
+ void func(int i) {
+ printf("%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
+ i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i);
+ }
+ int main() {
+ for (int i = 0; i < 1024; i++)
+ func(i);
+ printf("ok!\n");
+ return 0;
+ }
+ '''
+ Settings.TOTAL_STACK = 1024
+ self.do_run(src, 'ok!')
def test_array2(self):
src = '''
@@ -4715,6 +4721,15 @@ def process(filename):
post_build=post)
#build_ll_hook=self.do_autodebug)
+ # Second testcase, github issue 324
+ print '[issue 324]'
+ self.do_run(open(path_from_root('tests', 'freetype', 'main_2.c'), 'r').read(),
+ open(path_from_root('tests', 'freetype', 'ref_2.txt'), 'r').read(),
+ ['font.ttf', 'w', '32', '32', '25'],
+ libraries=self.get_freetype(),
+ includes=[path_from_root('tests', 'freetype', 'include')],
+ post_build=post)
+
def test_sqlite(self):
# gcc -O3 -I/home/alon/Dev/emscripten/tests/sqlite -ldl src.c
if self.emcc_args is None: return self.skip('Very slow without ta2, and we would also need to include dlmalloc manually without emcc')
@@ -6103,7 +6118,7 @@ Options that are modified or new in %s include:
assert ('assert(STACKTOP < STACK_MAX' in generated) == (opt_level == 0), 'assertions should be in opt == 0'
assert 'var $i;' in generated or 'var $i_01;' in generated or 'var $storemerge3;' in generated or 'var $storemerge4;' in generated or 'var $i_04;' in generated, 'micro opts should always be on'
if opt_level >= 1:
- assert 'HEAP8[HEAP32[' in generated or 'HEAP8[$vla1 + $storemerge4 / 2 | 0]' in generated or 'HEAP8[$vla1 + ($storemerge4 / 2 | 0)]' in generated or 'HEAP8[$vla1 + $i_04 / 2 | 0]' in generated or 'HEAP8[$vla1 + ($i_04 / 2 | 0)]' in generated or 'HEAP8[$1 + $i_01 / 2 | 0]' in generated or 'HEAP8[$1 + ($i_01 / 2 | 0)]' in generated, 'eliminator should create compound expressions, and fewer one-time vars'
+ assert 'HEAP8[HEAP32[' in generated or 'HEAP8[$vla1 + ($storemerge4 / 2 & -1) | 0]' in generated or 'HEAP8[$vla1 + ($storemerge4 / 2 & -1) | 0]' in generated or 'HEAP8[$vla1 + ($i_04 / 2 & -1) | 0]' in generated or 'HEAP8[$vla1 + ($i_04 / 2 & -1)]' in generated or 'HEAP8[$1 + ($i_01 / 2 & -1) | 0]' in generated or 'HEAP8[$1 + ($i_01 / 2 & -1) | 0]' in generated, 'eliminator should create compound expressions, and fewer one-time vars'
assert ('_puts(' in generated) == (opt_level >= 1), 'with opt >= 1, llvm opts are run and they should optimize printf to puts'
assert ('function _malloc(bytes) {' in generated) == (not has_malloc), 'If malloc is needed, it should be there, if not not'
assert 'function _main() {' in generated, 'Should be unminified, including whitespace'
@@ -6321,15 +6336,16 @@ f.close()
# by individual files
Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--preload-file', 'subdirr/data1.txt', '--preload-file', 'subdirr/moar/data2.txt', '-o', 'page.html']).communicate()
self.run_browser('page.html', 'You should see two cool numbers', '/report_result?1')
-
os.remove('page.html')
- # by directory
+ # by directory, and remove files to make sure
Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--preload-file', 'subdirr', '-o', 'page.html']).communicate()
+ shutil.rmtree(os.path.join(self.get_dir(), 'subdirr'))
self.run_browser('page.html', 'You should see two cool numbers', '/report_result?1')
def test_compressed_file(self):
open(os.path.join(self.get_dir(), 'datafile.txt'), 'w').write('compress this please' + (2000*'.'))
+ open(os.path.join(self.get_dir(), 'datafile2.txt'), 'w').write('moar' + (100*'!'))
open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(self.with_report_result(r'''
#include <stdio.h>
#include <string.h>
@@ -6342,17 +6358,23 @@ f.close()
fclose(f);
printf("file says: |%s|\n", buf);
int result = !strcmp("compress this please", buf);
+ FILE *f2 = fopen("datafile2.txt", "r");
+ fread(buf, 1, 5, f2);
+ buf[5] = 0;
+ fclose(f2);
+ result = result && !strcmp("moar!", buf);
+ printf("file 2 says: |%s|\n", buf);
REPORT_RESULT();
return 0;
}
'''))
- Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp'), '-o', 'page.html', '--preload-file', 'datafile.txt',
+ Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp'), '-o', 'page.html', '--preload-file', 'datafile.txt', '--preload-file', 'datafile2.txt',
'--compression', '%s,%s,%s' % (path_from_root('third_party', 'lzma.js', 'lzma-native'),
path_from_root('third_party', 'lzma.js', 'lzma-decoder.js'),
'LZMA.decompress')]).communicate()
assert os.path.exists(os.path.join(self.get_dir(), 'datafile.txt')), 'must be data file'
- assert os.path.exists(os.path.join(self.get_dir(), 'datafile.txt.compress')), 'must be data file in compressed form'
+ assert os.path.exists(os.path.join(self.get_dir(), 'page.data.compress')), 'must be data file in compressed form'
assert os.stat(os.path.join(self.get_dir(), 'page.js')).st_size != os.stat(os.path.join(self.get_dir(), 'page.js.compress')).st_size, 'compressed file must be different'
shutil.move(os.path.join(self.get_dir(), 'datafile.txt'), 'datafile.txt.renamedsoitcannotbefound');
self.run_browser('page.html', '', '/report_result?1')
@@ -6366,10 +6388,10 @@ f.close()
self.run_browser('page.html', '', '/report_result?600')
def test_sdl_image_compressed(self):
- for image, worth_compressing, width in [(path_from_root('tests', 'screenshot2.png'), True, 300),
- (path_from_root('tests', 'screenshot.jpg'), False, 600)]:
+ for image, width in [(path_from_root('tests', 'screenshot2.png'), 300),
+ (path_from_root('tests', 'screenshot.jpg'), 600)]:
self.clear()
- print image, worth_compressing
+ print image
basename = os.path.basename(image)
shutil.copyfile(image, os.path.join(self.get_dir(), basename))
@@ -6379,10 +6401,7 @@ f.close()
'--compression', '%s,%s,%s' % (path_from_root('third_party', 'lzma.js', 'lzma-native'),
path_from_root('third_party', 'lzma.js', 'lzma-decoder.js'),
'LZMA.decompress')]).communicate()
- assert ('.compress' in open('page.js').read()) == worth_compressing, 'do not compress image if not worth it'
- assert os.path.exists(basename + '.compress') == worth_compressing, 'remove .compress if not compressing'
- if worth_compressing:
- shutil.move(os.path.join(self.get_dir(), basename), basename + '.renamedsoitcannotbefound');
+ shutil.move(os.path.join(self.get_dir(), basename), basename + '.renamedsoitcannotbefound');
self.run_browser('page.html', '', '/report_result?' + str(width))
def test_sdl_canvas(self):
@@ -6451,6 +6470,15 @@ f.close()
Popen(['python', EMCC, os.path.join(self.get_dir(), 'sdl_mouse.c'), '-o', 'page.html', '--pre-js', 'pre.js']).communicate()
self.run_browser('page.html', '', '/report_result?740')
+ def test_sdl_audio(self):
+ shutil.copyfile(path_from_root('tests', 'sounds', 'alarmvictory_1.ogg'), os.path.join(self.get_dir(), 'sound.ogg'))
+ shutil.copyfile(path_from_root('tests', 'sounds', 'alarmcreatemiltaryfoot_1.wav'), os.path.join(self.get_dir(), 'sound2.wav'))
+ open(os.path.join(self.get_dir(), 'sdl_audio.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_audio.c')).read()))
+
+ # use closure to check for a possible bug with closure minifying away newer Audio() attributes
+ Popen(['python', EMCC, '-O2', '--minify', '0', os.path.join(self.get_dir(), 'sdl_audio.c'), '--preload-file', 'sound.ogg', '--preload-file', 'sound2.wav', '-o', 'page.html', '-s', 'EXPORTED_FUNCTIONS=["_main", "_play", "_play2"]']).communicate()
+ self.run_browser('page.html', '', '/report_result?1')
+
def test_worker(self):
# Test running in a web worker
output = Popen(['python', EMCC, path_from_root('tests', 'hello_world_worker.cpp'), '-o', 'worker.js'], stdout=PIPE, stderr=PIPE).communicate()
@@ -6665,6 +6693,36 @@ f.close()
output = Popen([NODE_JS, JS_OPTIMIZER, input] + passes, stdin=PIPE, stdout=PIPE).communicate()[0]
self.assertIdentical(expected, output.replace('\n\n', '\n'))
+ def test_m_mm(self):
+ open(os.path.join(self.get_dir(), 'foo.c'), 'w').write('''/* */''')
+ for opt in ['M', 'MM']:
+ output = Popen(['python', EMCC, os.path.join(self.get_dir(), 'foo.c'), '-' + opt], stdout=PIPE).communicate()[0]
+ assert 'foo.o: ' in output, '-%s failed to produce the right output: %s' % (opt, output)
+
+ def test_llvm_nativizer(self):
+ # avoid impure_ptr problems etc.
+ shutil.copyfile(path_from_root('tests', 'files.cpp'), os.path.join(self.get_dir(), 'files.cpp'))
+ open(os.path.join(self.get_dir(), 'somefile.binary'), 'w').write('''waka waka############################''')
+ open(os.path.join(self.get_dir(), 'test.file'), 'w').write('''ay file..............,,,,,,,,,,,,,,''')
+ open(os.path.join(self.get_dir(), 'stdin'), 'w').write('''inter-active''')
+ Popen(['python', EMCC, os.path.join(self.get_dir(), 'files.cpp'), '-c']).communicate()
+ Popen(['python', path_from_root('tools', 'nativize_llvm.py'), os.path.join(self.get_dir(), 'files.o')]).communicate(input)[0]
+ output = Popen([os.path.join(self.get_dir(), 'files.o.run')], stdin=open(os.path.join(self.get_dir(), 'stdin')), stdout=PIPE, stderr=PIPE).communicate()
+ self.assertIdentical('''size: 37
+data: 119,97,107,97,32,119,97,107,97,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35
+loop: 119 97 107 97 32 119 97 107 97 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35
+input:inter-active
+texto
+$
+5 : 10,30,20,11,88
+other=ay file...
+seeked= file.
+seeked=e...
+seeked=,,.
+fscanfed: 10 - hello
+''', output[0])
+ self.assertIdentical('texte\n', output[1])
+
elif 'benchmark' in str(sys.argv):
# Benchmarks. Run them with argument |benchmark|. To run a specific test, do
# |benchmark.test_X|.
diff --git a/tests/sdl_audio.c b/tests/sdl_audio.c
new file mode 100644
index 00000000..77c82b4e
--- /dev/null
+++ b/tests/sdl_audio.c
@@ -0,0 +1,50 @@
+#include <stdio.h>
+#include <SDL/SDL.h>
+#include <SDL/SDL_mixer.h>
+#include <assert.h>
+#include <emscripten.h>
+
+Mix_Chunk *sound, *sound2;
+
+void play2();
+
+void play() {
+ int channel = Mix_PlayChannel(-1, sound, 1);
+ assert(channel >= 0);
+
+ emscripten_run_script("setTimeout(Module['_play2'], 1000)");
+}
+
+void play2() {
+ int channel2 = Mix_PlayChannel(-1, sound2, 1);
+ assert(channel2 >= 0);
+}
+
+int main(int argc, char **argv) {
+ SDL_Init(SDL_INIT_AUDIO);
+
+ int ret = Mix_OpenAudio(0, 0, 0, 0); // we ignore all these..
+ assert(ret == 0);
+
+ sound = Mix_LoadWAV("sound.ogg");
+ assert(sound);
+ sound2 = Mix_LoadWAV("sound2.wav");
+ assert(sound);
+
+ play();
+ if (argc == 12121) play2(); // keep it alive
+
+ emscripten_run_script("element = document.createElement('input');"
+ "element.setAttribute('type', 'button');"
+ "element.setAttribute('value', 'replay!');"
+ "element.setAttribute('onclick', 'Module[\"_play\"]()');"
+ "document.body.appendChild(element);");
+
+ printf("you should hear two sounds. press the button to replay!\n");
+
+ int result = 1;
+ REPORT_RESULT();
+
+ return 0;
+}
+
diff --git a/tests/sdl_canvas.c b/tests/sdl_canvas.c
index aaa9d653..ab1340a8 100644
--- a/tests/sdl_canvas.c
+++ b/tests/sdl_canvas.c
@@ -12,11 +12,11 @@ int main() {
TTF_Font *font = TTF_OpenFont("myfont.ttf", 40);
printf("Font: %p\n", font);
- SDL_Color color = { 0xff, 0x99, 0x00, 0xb0 };
+ SDL_Color color = { 0xff, 0x99, 0x00, 0xff };
- SDL_Surface *text = TTF_RenderText_Solid(font, "hello faint orange world", color);
+ SDL_Surface *text = TTF_RenderText_Solid(font, "hello orange world", color);
- SDL_Color color2 = { 0xbb, 0, 0xff, 0 };
+ SDL_Color color2 = { 0xbb, 0, 0xff, 0xff };
SDL_Surface *text2 = TTF_RenderText_Solid(font, "a second line, purple", color2);
// render
@@ -27,7 +27,7 @@ int main() {
// fill stuff
SDL_Rect rect = { 200, 200, 175, 125 };
- SDL_FillRect(screen, &rect, 0x2222ff00);
+ SDL_FillRect(screen, &rect, 0x2222ffff);
SDL_Flip(screen);
diff --git a/tests/sdl_mouse.c b/tests/sdl_mouse.c
index a0520839..dae3f636 100644
--- a/tests/sdl_mouse.c
+++ b/tests/sdl_mouse.c
@@ -44,7 +44,7 @@ int main() {
SDL_Surface *screen = SDL_SetVideoMode(600, 450, 32, SDL_HWSURFACE);
SDL_Rect rect = { 0, 0, 600, 450 };
- SDL_FillRect(screen, &rect, 0x2244ff00);
+ SDL_FillRect(screen, &rect, 0x2244ffff);
emscripten_run_script("simulateMouseEvent(10, 20, -1)"); // move from 0,0 to 10,20
emscripten_run_script("simulateMouseEvent(10, 20, 0)"); // click
diff --git a/tests/sounds/LICENSE.txt b/tests/sounds/LICENSE.txt
new file mode 100644
index 00000000..1525d517
--- /dev/null
+++ b/tests/sounds/LICENSE.txt
@@ -0,0 +1,19 @@
+The files in this directory are Copyright (C) 2009 Wildfire Games.
+
+These files are licensed under the Creative Commons Attribution-Share Alike 3.0
+(CC-by-sa) license, available at http://creativecommons.org/licenses/by-sa/3.0/
+
+Briefly, this means:
+
+* You may use, modify and distribute these files, for commercial and
+ non-commercial purposes.
+
+* If you distribute one of these files, you must include attribution (e.g.
+ in the credits screen of a game or a video, or in a text file accompanying
+ the files). The attribution must include:
+ * A link to http://creativecommons.org/licenses/by-sa/3.0/
+ * The name "Wildfire Games" as the original author
+ * A link to http://www.wildfiregames.com/
+
+* If you distribute one of these files, you must release it (and any
+ modifications you have made to it) under the CC-by-sa license. \ No newline at end of file
diff --git a/tests/sounds/alarmcreatemiltaryfoot_1.wav b/tests/sounds/alarmcreatemiltaryfoot_1.wav
new file mode 100644
index 00000000..2de6314a
--- /dev/null
+++ b/tests/sounds/alarmcreatemiltaryfoot_1.wav
Binary files differ
diff --git a/tests/sounds/alarmvictory_1.ogg b/tests/sounds/alarmvictory_1.ogg
new file mode 100644
index 00000000..cadb1539
--- /dev/null
+++ b/tests/sounds/alarmvictory_1.ogg
Binary files differ
diff --git a/tools/eliminator/eliminator.coffee b/tools/eliminator/eliminator.coffee
index b941ccc0..a4c98930 100644
--- a/tools/eliminator/eliminator.coffee
+++ b/tools/eliminator/eliminator.coffee
@@ -19,6 +19,7 @@
# Imports.
uglify = require 'uglify-js'
fs = require 'fs'
+os = require 'os'
# Functions which have been generated by Emscripten. We optimize only those.
generatedFunctions = []
@@ -369,12 +370,14 @@ class ExpressionOptimizer
# function, then writes the optimized result to stdout.
main = ->
# Get the parse tree.
- src = fs.readFileSync('/dev/stdin').toString()
- # The following seems to work on windows, but fails on linux..
- #src = ''
- #size = fs.fstatSync(process.stdin.fd).size
- #if size > 0
- # src = fs.readSync(process.stdin.fd, size)[0]
+ if os.platform().substr(0, 3) != 'win'
+ src = fs.readFileSync('/dev/stdin').toString()
+ else
+ # The following seems to work on windows, but fails on linux..
+ src = ''
+ size = fs.fstatSync(process.stdin.fd).size
+ if size > 0
+ src = fs.readSync(process.stdin.fd, size)[0]
throw 'Cannot identify generated functions' if GENERATED_FUNCTIONS_MARKER in src
generatedFunctionsLine = src.split('\n').filter (line) ->
diff --git a/tools/shared.py b/tools/shared.py
index 61ec912e..4e5f3d6a 100644
--- a/tools/shared.py
+++ b/tools/shared.py
@@ -1,4 +1,4 @@
-import shutil, time, os, sys, json, tempfile, copy
+import shutil, time, os, sys, json, tempfile, copy, shlex
from subprocess import Popen, PIPE, STDOUT
from tempfile import mkstemp
@@ -184,6 +184,8 @@ else:
if 'gcparam' not in str(SPIDERMONKEY_ENGINE):
SPIDERMONKEY_ENGINE += ['-e', "gcparam('maxBytes', 1024*1024*1024);"] # Our very large files need lots of gc heap
+WINDOWS = 'win' in sys.platform
+
# Temp file utilities
def try_delete(filename):
@@ -360,11 +362,11 @@ class Building:
@staticmethod
def get_building_env():
env = os.environ.copy()
- env['CC'] = '%s' % EMCC
- env['CXX'] = '%s' % EMXX
- env['AR'] = '%s' % EMAR
- env['RANLIB'] = '%s' % EMRANLIB
- env['LIBTOOL'] = '%s' % EMLIBTOOL
+ env['CC'] = EMCC if not WINDOWS else 'python %r' % EMCC
+ env['CXX'] = EMXX if not WINDOWS else 'python %r' % EMXX
+ env['AR'] = EMAR if not WINDOWS else 'python %r' % EMAR
+ env['RANLIB'] = EMRANLIB if not WINDOWS else 'python %r' % EMRANLIB
+ env['LIBTOOL'] = EMLIBTOOL if not WINDOWS else 'python %r' % EMLIBTOOL
env['EMMAKEN_COMPILER'] = Building.COMPILER
env['EMSCRIPTEN_TOOLS'] = path_from_root('tools')
env['CFLAGS'] = env['EMMAKEN_CFLAGS'] = ' '.join(Building.COMPILER_TEST_OPTS)
@@ -376,14 +378,14 @@ class Building:
@staticmethod
def handle_CMake_toolchain(args, env):
- CMakeToolchain = '''# the name of the target operating system
+ CMakeToolchain = ('''# the name of the target operating system
SET(CMAKE_SYSTEM_NAME Linux)
# which C and C++ compiler to use
-SET(CMAKE_C_COMPILER $EMSCRIPTEN_ROOT/emcc)
-SET(CMAKE_CXX_COMPILER $EMSCRIPTEN_ROOT/em++)
-SET(CMAKE_AR $EMSCRIPTEN_ROOT/emar)
-SET(CMAKE_RANLIB $EMSCRIPTEN_ROOT/emranlib)
+SET(CMAKE_C_COMPILER %(winfix)s$EMSCRIPTEN_ROOT/emcc)
+SET(CMAKE_CXX_COMPILER %(winfix)s$EMSCRIPTEN_ROOT/em++)
+SET(CMAKE_AR %(winfix)s$EMSCRIPTEN_ROOT/emar)
+SET(CMAKE_RANLIB %(winfix)s$EMSCRIPTEN_ROOT/emranlib)
SET(CMAKE_C_FLAGS $CFLAGS)
SET(CMAKE_CXX_FLAGS $CXXFLAGS)
@@ -396,7 +398,7 @@ SET(CMAKE_FIND_ROOT_PATH $EMSCRIPTEN_ROOT/system/include )
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH)
-set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' \
+set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS else 'python ' }) \
.replace('$EMSCRIPTEN_ROOT', path_from_root('').replace('\\', '/')) \
.replace('$CFLAGS', env['CFLAGS']) \
.replace('$CXXFLAGS', env['CFLAGS'])
@@ -743,7 +745,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' \
#'--variable_map_output_file', filename + '.vars',
'--js', filename, '--js_output_file', filename + '.cc.js']
if os.environ.get('EMCC_CLOSURE_ARGS'):
- args += os.environ.get('EMCC_CLOSURE_ARGS').split(' ')
+ args += shlex.split(os.environ.get('EMCC_CLOSURE_ARGS'))
process = Popen(args, stdout=PIPE, stderr=STDOUT)
cc_output = process.communicate()[0]
if process.returncode != 0 or not os.path.exists(filename + '.cc.js'):
diff --git a/tools/test-js-optimizer-output.js b/tools/test-js-optimizer-output.js
index ca76cae1..273efdb7 100644
--- a/tools/test-js-optimizer-output.js
+++ b/tools/test-js-optimizer-output.js
@@ -311,4 +311,7 @@ function notComps() {
shoo();
}
}
-// EMSCRIPTEN_GENERATED_FUNCTIONS: ["abc", "xyz", "xyz2", "expr", "loopy", "bits", "maths", "hoisting", "demangle", "lua", "moreLabels", "notComps"]
+function tricky() {
+ var $conv642 = $conv6374 - (($132 << 16 >> 16) / 2 & -1) & 65535;
+}
+// EMSCRIPTEN_GENERATED_FUNCTIONS: ["abc", "xyz", "xyz2", "expr", "loopy", "bits", "maths", "hoisting", "demangle", "lua", "moreLabels", "notComps", "tricky"]
diff --git a/tools/test-js-optimizer.js b/tools/test-js-optimizer.js
index 18c9ac75..e9d973dd 100644
--- a/tools/test-js-optimizer.js
+++ b/tools/test-js-optimizer.js
@@ -422,4 +422,8 @@ function notComps() {
shoo();
}
}
-// EMSCRIPTEN_GENERATED_FUNCTIONS: ["abc", "xyz", "xyz2", "expr", "loopy", "bits", "maths", "hoisting", "demangle", "lua", "moreLabels", "notComps"]
+function tricky() {
+ // The &-1 is a rounding correction, and must not be removed
+ var $conv642 = ($conv6374 - (($132 << 16 >> 16 | 0) / 2 & -1) | 0) & 65535;
+}
+// EMSCRIPTEN_GENERATED_FUNCTIONS: ["abc", "xyz", "xyz2", "expr", "loopy", "bits", "maths", "hoisting", "demangle", "lua", "moreLabels", "notComps", "tricky"]