Merge branch 'incoming' into proxyGL
This commit is contained in:
Коммит
ba387574f2
2
AUTHORS
2
AUTHORS
|
@ -144,4 +144,4 @@ a license to everyone to use it as detailed in LICENSE.)
|
|||
* Jason Green <jason@transgaming.com> (copyright owned by TransGaming, Inc.)
|
||||
* Ningxin Hu <ningxin.hu@intel.com> (copyright owned by Intel)
|
||||
* Nicolas Guillemot <nlguillemot@gmail.com>
|
||||
|
||||
* Sathyanarayanan Gunasekaran <gsathya.ceg@gmail.com> (copyright owned by Mozilla Foundation)
|
||||
|
|
141
ChangeLog
141
ChangeLog
|
@ -10,9 +10,144 @@ Not all changes are documented here. In particular, new features, user-oriented
|
|||
Current trunk code
|
||||
------------------
|
||||
- To see a list of commits in the active development branch 'incoming', which have not yet been packaged in a release, see
|
||||
- Emscripten: https://github.com/kripken/emscripten/compare/1.16.0...incoming
|
||||
- Emscripten-LLVM: https://github.com/kripken/emscripten-fastcomp/compare/1.16.0...incoming
|
||||
- Emscripten-Clang: https://github.com/kripken/emscripten-fastcomp-clang/compare/1.16.0...incoming
|
||||
- Emscripten: https://github.com/kripken/emscripten/compare/1.20.0...incoming
|
||||
- Emscripten-LLVM: https://github.com/kripken/emscripten-fastcomp/compare/1.20.0...incoming
|
||||
- Emscripten-Clang: https://github.com/kripken/emscripten-fastcomp-clang/compare/1.20.0...incoming
|
||||
|
||||
v1.20.0: 6/13/2014
|
||||
------------------
|
||||
- Optimize in-memory virtual filesystem performance when serialized to an IndexedDB.
|
||||
- Fixed memcpy regression with ta0 and ta1 modes.
|
||||
- Fixed an issue with line numbers being messed up when generating source maps (#2410)
|
||||
- Fixed an ffdb logging bug that could cause it to drop messages if they were being received too fast. Added support getting memory and system descriptions with ffdb.
|
||||
- Added a new extension to SDL "emscripten_SDL_SetEventHandler()" which enabled application to perform SDL event handling inside a JS event handler to overcome browser security restrictions. (#2417)
|
||||
- Full list of changes:
|
||||
- Emscripten: https://github.com/kripken/emscripten/compare/1.19.2...1.20.0
|
||||
- Emscripten-LLVM: no changes.
|
||||
- Emscripten-Clang: no changes.
|
||||
|
||||
v1.19.2: 6/9/2014
|
||||
------------------
|
||||
- Updated CMake support for response file handling.
|
||||
- Fixed issues with glfwGetProcAddress and glfwSetWindowSizeCallback.
|
||||
- Fixed an issue with regexes that caused issues on IE11 runtime (#2400)
|
||||
- Added a new functions emscripten_get_preloaded_image_data() and emscripten_get_preloaded_image_data_from_FILE() to obtain pixel data of preloaded images.
|
||||
- Greatly improved ffdb capabilities to operate a FFOS device.
|
||||
- Fixed a Windows-specific bug where the user temp directory was littered with temporary .rsp files that did not get cleaned up.
|
||||
- Improved SIMD support.
|
||||
- Full list of changes:
|
||||
- Emscripten: https://github.com/kripken/emscripten/compare/1.19.1...1.19.2
|
||||
- Emscripten-LLVM: https://github.com/kripken/emscripten-fastcomp/compare/1.19.1...1.19.2
|
||||
- Emscripten-Clang: no changes.
|
||||
|
||||
v1.19.1: 6/3/2014
|
||||
------------------
|
||||
- Migrate to using musl sscanf and sprintf and the family that writes to memory, and not directly to the filesystem.
|
||||
- Improve the error messages from -s SAFE_HEAP_ACCESS=1 runtime checks.
|
||||
- Added new linker flag -s NO_DYNAMIC_EXECUTION=1 which removes the use of eval() and new Function() in the generated output. For more information, see "Eval and related functions are disabled" in https://developer.chrome.com/extensions/contentSecurityPolicy .
|
||||
- Fixed a compiler issue when very large double constants are present. (#2392)
|
||||
- Full list of changes:
|
||||
- Emscripten: https://github.com/kripken/emscripten/compare/1.19.0...1.19.1
|
||||
- Emscripten-LLVM: no changes.
|
||||
- Emscripten-Clang: no changes.
|
||||
|
||||
v1.19.0: 5/29/2014
|
||||
------------------
|
||||
- Added an error message to signal that linkable modules are not supported in fastcomp.
|
||||
- Fixed a miscompilation issue that resulted in an error "SyntaxError: invalid increment operand" and a statement +(+0) being generated (#2314)
|
||||
- Make optimized compiler output smaller by running the shell code through uglify when not using closure.
|
||||
- Fixed a crash in SDL audio loading code introduced in v1.18.3
|
||||
- Fixed an issue where glTex(Sub)Image2D might throw an exception on error, instead of setting glGetError().
|
||||
- Added new typedefs emscripten_align1_short, emscripten_align{1/2}_int, emscripten_align{1/2}_float and emscripten_align{1/2/4}_double to ease signaling the compiler that unaligned data is present. (#2378)
|
||||
- Fixed an embind issue with refcount tracking on smart pointers.
|
||||
- Full list of changes:
|
||||
- Emscripten: https://github.com/kripken/emscripten/compare/1.18.4...1.19.0
|
||||
- Emscripten-LLVM: https://github.com/kripken/emscripten-fastcomp/compare/1.18.4...1.19.0
|
||||
- Emscripten-Clang: no changes.
|
||||
|
||||
v1.18.4: 5/27/2014
|
||||
------------------
|
||||
- Fixed error message on unsupported linking options (#2365)
|
||||
- Updated embind to latest version from IMVU upstream.
|
||||
- Fixed an issue where source maps did not load properly in Firefox.
|
||||
- Added a more descriptive error message to fastcomp when MAX_SETJMPS limit is violated. (#2379)
|
||||
- Full list of changes:
|
||||
- Emscripten: https://github.com/kripken/emscripten/compare/1.18.3...1.18.4
|
||||
- Emscripten-LLVM: https://github.com/kripken/emscripten-fastcomp/compare/1.18.3...1.18.4
|
||||
- Emscripten-Clang: no changes.
|
||||
|
||||
v1.18.3: 5/21/2014
|
||||
------------------
|
||||
- Added support to emcc command line for "archive groups": -Wl,--start-group and -Wl,--end-group
|
||||
- Greatly optimized ccall and cwrap implementations.
|
||||
- Added new support for SDL_Mix backend to use WebAudio to play back audio clips.
|
||||
- Fixed a registerizeHarder issue with elimination of conditional expressions.
|
||||
- Migrated single-character standard C functions (islower, tolower, and the family) to use musl implementations.
|
||||
- Updated relooper to not optimize out breaks if it causes excessive nesting.
|
||||
- Full list of changes:
|
||||
- Emscripten: https://github.com/kripken/emscripten/compare/1.18.2...1.18.3
|
||||
- Emscripten-LLVM: https://github.com/kripken/emscripten-fastcomp/compare/1.18.2...1.18.3
|
||||
- Emscripten-Clang: no changes.
|
||||
|
||||
v1.18.2: 5/19/2014
|
||||
------------------
|
||||
- Fixed a problem which blocked user applications from handling WebGL context loss events themselves.
|
||||
- Added a new HTML5 api function emscripten_is_webgl_context_lost() which allows polling for context loss in addition to receiving events.
|
||||
- Improved async wget progress events to work better across browsers.
|
||||
- Improved WebIDL binder support.
|
||||
- Added new typeof() function to emscripten::val.
|
||||
- Added support for SDL window events SDL_WINDOWEVENT_FOCUS_GAINED, SDL_WINDOWEVENT_FOCUS_LOST, SDL_WINDOWEVENT_SHOWN, SDL_WINDOWEVENT_HIDDEN.
|
||||
- Fixed a compiler miscompilation on unsigned i1 bitcasts (#2350)
|
||||
- Fixed a compiler bug where doubles in varargs might not get 8-byte aligned (#2358)
|
||||
- Full list of changes:
|
||||
- Emscripten: https://github.com/kripken/emscripten/compare/1.18.1...1.18.2
|
||||
- Emscripten-LLVM: https://github.com/kripken/emscripten-fastcomp/compare/1.18.1...1.18.2
|
||||
- Emscripten-Clang: no changes.
|
||||
|
||||
v1.18.1: 5/12/2014
|
||||
------------------
|
||||
- Fixed an issue where the mouse wheel scroll did not work with SDL.
|
||||
- Fixed an issue with emscripten_async_wget, which undesirably expected that the string pointer passed to it stayed alive for the duration of the operation (#2349)
|
||||
- Emscripten now issues a warning message when the EXPORTED_FUNCTIONS list contains invalid symbol names (#2338)
|
||||
- Full list of changes:
|
||||
- Emscripten: https://github.com/kripken/emscripten/compare/1.18.0...1.18.1
|
||||
- Emscripten-LLVM: no changes.
|
||||
- Emscripten-Clang: no changes.
|
||||
|
||||
v1.18.0: 5/10/2014
|
||||
------------------
|
||||
- Enable support for low-level C<->JS interop to marshall 64 bit integers from C to JS.
|
||||
- Fixed an issue that caused some programs to immediately run out of memory "(cannot enlarge memory arrays)" at startup. (#2334)
|
||||
- Fixed a crash issue with generated touch events that didn't correspond to a real touch.
|
||||
- Full list of changes:
|
||||
- Emscripten: https://github.com/kripken/emscripten/compare/1.17.0...1.18.0
|
||||
- Emscripten-LLVM: https://github.com/kripken/emscripten-fastcomp/compare/1.17.0...1.18.0
|
||||
- Emscripten-Clang: no changes.
|
||||
|
||||
v1.17.0: 5/6/2014
|
||||
------------------
|
||||
- Enabled asm.js compilation and -s PRECISE_F32 support when using embind.
|
||||
- Improved relooper to emit switches in many-entried blocks.
|
||||
- Fixed a GLFW bug where mouse wheel direction was reversed.
|
||||
- Fixed glfwGetKey to work even when no callback is registered with glfwGetKeyCallback (#1320)
|
||||
- Added a new tool 'webidl_binder' that generates C <-> JS interop code from WebIDL descriptions.
|
||||
- Fix emscripten compilation to work on pages that don't contain a HTML canvas.
|
||||
- Added a new error message to default shell when an uncaught exception is thrown.
|
||||
- Improved error diagnostics reported by -s SAFE_HEAP=1.
|
||||
- Added support for registering callbacks hook to VFS file open, write, move, close and delete.
|
||||
- Added embind support to std::basic_string<unsigned char>
|
||||
- By default, the C runtime will no longer exit after returning from main() when safeSetTimeout() or safeSetInterval() is used.
|
||||
- Fixed an issue with sscanf formatting (#2322)
|
||||
- Fixed an issue where precompiled headers were given a wrong output filename (#2320)
|
||||
- Enabled registerizeHarder optimization pass to work when outlining is enabled.
|
||||
- Fixed an issue with strptime month handling (#2324)
|
||||
- Added an initial implementation of a new tool 'ffdb' which can be used to operate a Firefox OS phone from the command line.
|
||||
- Fixed a compiler crash on assertion failure '!contains(BranchesOut, Target)' (emscripten-fastcomp #32)
|
||||
- Added a new ABI to Clang that targets Emscripten specifically. Stop aligning member functions to save some space in the function table array.
|
||||
- Full list of changes:
|
||||
- Emscripten: https://github.com/kripken/emscripten/compare/1.16.0...1.17.0
|
||||
- Emscripten-LLVM: https://github.com/kripken/emscripten-fastcomp/compare/1.16.0...1.17.0
|
||||
- Emscripten-Clang: https://github.com/kripken/emscripten-fastcomp-clang/compare/1.16.0...1.17.0
|
||||
|
||||
v1.16.0: 4/16/2014
|
||||
------------------
|
||||
|
|
4
emcc
4
emcc
|
@ -1376,6 +1376,8 @@ try:
|
|||
if shared.Settings.ASM_JS and shared.Settings.DLOPEN_SUPPORT:
|
||||
assert shared.Settings.DISABLE_EXCEPTION_CATCHING, 'no exceptions support with dlopen in asm yet'
|
||||
|
||||
assert not (bind and shared.Settings.NO_DYNAMIC_EXECUTION), 'NO_DYNAMIC_EXECUTION disallows embind'
|
||||
|
||||
if proxy_to_worker:
|
||||
shared.Settings.PROXY_TO_WORKER = 1
|
||||
|
||||
|
@ -1692,7 +1694,7 @@ try:
|
|||
logging.debug('wrote memory initialization to %s', memfile)
|
||||
else:
|
||||
logging.debug('did not see memory initialization')
|
||||
elif shared.Settings.USE_TYPED_ARRAYS == 2 and not shared.Settings.MAIN_MODULE and not shared.Settings.SIDE_MODULE:
|
||||
elif shared.Settings.USE_TYPED_ARRAYS == 2 and not shared.Settings.MAIN_MODULE and not shared.Settings.SIDE_MODULE and debug_level < 4:
|
||||
# not writing a binary init, but we can at least optimize them by splitting them up
|
||||
src = open(final).read()
|
||||
src = shared.JS.optimize_initializer(src)
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
1.19.1
|
||||
1.20.0
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
"SDL_Init": ["malloc", "free"],
|
||||
"SDL_GL_GetProcAddress": ["emscripten_GetProcAddress"],
|
||||
"eglGetProcAddress": ["emscripten_GetProcAddress"],
|
||||
"glfwGetProcAddress": ["emscripten_GetProcAddress"],
|
||||
"emscripten_GetProcAddress": ["strstr"]
|
||||
}
|
||||
|
||||
|
|
|
@ -8,13 +8,14 @@ var TIME = 10000;
|
|||
Date.now = function() {
|
||||
return TIME++;
|
||||
};
|
||||
performance.now = Date.now;
|
||||
if (typeof performance === 'object') performance.now = Date.now;
|
||||
|
||||
function hashMemory(id) {
|
||||
var ret = 0;
|
||||
for (var i = 0; i < HEAPU8.length; i++) {
|
||||
var len = Math.max(DYNAMICTOP, STATICTOP);
|
||||
for (var i = 0; i < len; i++) {
|
||||
ret = (ret*17 + HEAPU8[i])|0;
|
||||
}
|
||||
print(id + ':' + ret);
|
||||
printErr(id + ':' + ret);
|
||||
}
|
||||
|
|
@ -370,7 +370,7 @@ function JSify(data, functionsOnly) {
|
|||
|
||||
// name the function; overwrite if it's already named
|
||||
snippet = snippet.replace(/function(?:\s+([^(]+))?\s*\(/, 'function _' + ident + '(');
|
||||
if (LIBRARY_DEBUG) {
|
||||
if (LIBRARY_DEBUG && !LibraryManager.library[ident + '__asm']) {
|
||||
snippet = snippet.replace('{', '{ var ret = (function() { if (Runtime.debug) Module.printErr("[library call:' + ident + ': " + Array.prototype.slice.call(arguments).map(Runtime.prettyPrint) + "]"); ');
|
||||
snippet = snippet.substr(0, snippet.length-1) + '}).apply(this, arguments); if (Runtime.debug && typeof ret !== "undefined") Module.printErr(" [ return:" + Runtime.prettyPrint(ret)); return ret; \n}';
|
||||
}
|
||||
|
@ -1902,6 +1902,9 @@ function JSify(data, functionsOnly) {
|
|||
print(read('webGLWorker.js'));
|
||||
print(read('proxyWorker.js'));
|
||||
}
|
||||
if (DETERMINISTIC) {
|
||||
print(read('deterministic.js'));
|
||||
}
|
||||
if (RUNTIME_TYPE_INFO) {
|
||||
Types.cleanForRuntime();
|
||||
print('Runtime.typeInfo = ' + JSON.stringify(Types.types));
|
||||
|
|
|
@ -3166,7 +3166,7 @@ LibraryManager.library = {
|
|||
#endif
|
||||
environ: 'allocate(1, "i32*", ALLOC_STATIC)',
|
||||
__environ__deps: ['environ'],
|
||||
__environ: '_environ',
|
||||
__environ: 'environ',
|
||||
__buildEnvironment__deps: ['__environ'],
|
||||
__buildEnvironment: function(env) {
|
||||
// WARNING: Arbitrary limit!
|
||||
|
@ -3388,9 +3388,20 @@ LibraryManager.library = {
|
|||
memcpy__sig: 'iiii',
|
||||
memcpy__deps: ['emscripten_memcpy_big'],
|
||||
memcpy: function(dest, src, num) {
|
||||
#if USE_TYPED_ARRAYS == 0
|
||||
{{{ makeCopyValues('dest', 'src', 'num', 'null') }}};
|
||||
return num;
|
||||
#endif
|
||||
#if USE_TYPED_ARRAYS == 1
|
||||
{{{ makeCopyValues('dest', 'src', 'num', 'null') }}};
|
||||
return num;
|
||||
#endif
|
||||
|
||||
dest = dest|0; src = src|0; num = num|0;
|
||||
var ret = 0;
|
||||
#if USE_TYPED_ARRAYS
|
||||
if ((num|0) >= 4096) return _emscripten_memcpy_big(dest|0, src|0, num|0)|0;
|
||||
#endif
|
||||
ret = dest|0;
|
||||
if ((dest&3) == (src&3)) {
|
||||
while (dest & 3) {
|
||||
|
|
|
@ -1175,6 +1175,39 @@ mergeInto(LibraryManager.library, {
|
|||
var info = Browser.workers[id];
|
||||
if (!info) return -1;
|
||||
return info.awaited;
|
||||
},
|
||||
|
||||
emscripten_get_preloaded_image_data: function(path, w, h) {
|
||||
if (typeof path === "number") {
|
||||
path = Pointer_stringify(path);
|
||||
}
|
||||
|
||||
path = PATH.resolve(path);
|
||||
|
||||
var canvas = Module["preloadedImages"][path];
|
||||
if (canvas) {
|
||||
var ctx = canvas.getContext("2d");
|
||||
var image = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
var buf = _malloc(canvas.width * canvas.height * 4);
|
||||
|
||||
HEAPU8.set(image.data, buf);
|
||||
|
||||
{{{ makeSetValue('w', '0', 'canvas.width', 'i32') }}};
|
||||
{{{ makeSetValue('h', '0', 'canvas.height', 'i32') }}};
|
||||
return buf;
|
||||
}
|
||||
|
||||
return 0;
|
||||
},
|
||||
|
||||
emscripten_get_preloaded_image_data_from_FILE__deps: ['emscripten_get_preloaded_image_data'],
|
||||
emscripten_get_preloaded_image_data_from_FILE: function(file, w, h) {
|
||||
var stream = FS.getStreamFromPtr(file);
|
||||
if (stream) {
|
||||
return _emscripten_get_preloaded_image_data(stream.path, w, h);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -1077,6 +1077,10 @@ mergeInto(LibraryManager.library, {
|
|||
if (!stream.stream_ops.write) {
|
||||
throw new FS.ErrnoError(ERRNO_CODES.EINVAL);
|
||||
}
|
||||
if (stream.flags & {{{ cDefine('O_APPEND') }}}) {
|
||||
// seek to the end before writing in append mode
|
||||
FS.llseek(stream, 0, {{{ cDefine('SEEK_END') }}});
|
||||
}
|
||||
var seeking = true;
|
||||
if (typeof position === 'undefined') {
|
||||
position = stream.position;
|
||||
|
@ -1084,10 +1088,6 @@ mergeInto(LibraryManager.library, {
|
|||
} else if (!stream.seekable) {
|
||||
throw new FS.ErrnoError(ERRNO_CODES.ESPIPE);
|
||||
}
|
||||
if (stream.flags & {{{ cDefine('O_APPEND') }}}) {
|
||||
// seek to the end before writing in append mode
|
||||
FS.llseek(stream, 0, {{{ cDefine('SEEK_END') }}});
|
||||
}
|
||||
var bytesWritten = stream.stream_ops.write(stream, buffer, offset, length, position, canOwn);
|
||||
if (!seeking) stream.position += bytesWritten;
|
||||
try {
|
||||
|
@ -1480,6 +1480,7 @@ mergeInto(LibraryManager.library, {
|
|||
// WARNING: Can't read binary files in V8's d8 or tracemonkey's js, as
|
||||
// read() will try to parse UTF8.
|
||||
obj.contents = intArrayFromString(Module['read'](obj.url), true);
|
||||
obj.usedBytes = obj.contents.length;
|
||||
} catch (e) {
|
||||
success = false;
|
||||
}
|
||||
|
@ -1601,6 +1602,10 @@ mergeInto(LibraryManager.library, {
|
|||
node.contents = null;
|
||||
node.url = properties.url;
|
||||
}
|
||||
// Add a function that defers querying the file size until it is asked the first time.
|
||||
Object.defineProperty(node, "usedBytes", {
|
||||
get: function() { return this.contents.length; }
|
||||
});
|
||||
// override each stream op with one that tries to force load the lazy file first
|
||||
var stream_ops = {};
|
||||
var keys = Object.keys(node.stream_ops);
|
||||
|
|
|
@ -417,6 +417,9 @@ var LibraryGLFW = {
|
|||
|
||||
glfwSetWindowSizeCallback: function(cbfun) {
|
||||
GLFW.resizeFunc = cbfun;
|
||||
if (GLFW.resizeFunc) {
|
||||
Runtime.dynCall('vii', GLFW.resizeFunc, [Module['canvas'].width, Module['canvas'].height]);
|
||||
}
|
||||
},
|
||||
|
||||
glfwSetWindowCloseCallback: function(cbfun) {
|
||||
|
@ -507,9 +510,9 @@ var LibraryGLFW = {
|
|||
return Module.ctx.getSupportedExtensions().indexOf(Pointer_stringify(extension)) > -1;
|
||||
},
|
||||
|
||||
glfwGetProcAddress__deps: ['glfwGetProcAddress'],
|
||||
glfwGetProcAddress__deps: ['emscripten_GetProcAddress'],
|
||||
glfwGetProcAddress: function(procname) {
|
||||
return _getProcAddress(procname);
|
||||
return _emscripten_GetProcAddress(procname);
|
||||
},
|
||||
|
||||
glfwGetGLVersion: function(major, minor, rev) {
|
||||
|
|
|
@ -135,6 +135,9 @@ mergeInto(LibraryManager.library, {
|
|||
if (FS.isDir(stat.mode)) {
|
||||
return callback(null, { timestamp: stat.mtime, mode: stat.mode });
|
||||
} else if (FS.isFile(stat.mode)) {
|
||||
// Performance consideration: storing a normal JavaScript array to a IndexedDB is much slower than storing a typed array.
|
||||
// Therefore always convert the file contents to a typed array first before writing the data to IndexedDB.
|
||||
node.contents = MEMFS.getFileDataAsTypedArray(node);
|
||||
return callback(null, { timestamp: stat.mtime, mode: stat.mode, contents: node.contents });
|
||||
} else {
|
||||
return callback(new Error('node type not supported'));
|
||||
|
|
|
@ -2,11 +2,6 @@ mergeInto(LibraryManager.library, {
|
|||
$MEMFS__deps: ['$FS'],
|
||||
$MEMFS: {
|
||||
ops_table: null,
|
||||
|
||||
// content modes
|
||||
CONTENT_OWNING: 1, // contains a subarray into the heap, and we own it, without copying (note: someone else needs to free() it, if that is necessary)
|
||||
CONTENT_FLEXIBLE: 2, // has been modified or never set to anything, and is a flexible js array that can grow/shrink
|
||||
CONTENT_FIXED: 3, // contains some fixed-size content written into it, in a typed array
|
||||
mount: function(mount) {
|
||||
return MEMFS.createNode(null, '/', {{{ cDefine('S_IFDIR') }}} | 511 /* 0777 */, 0);
|
||||
},
|
||||
|
@ -71,8 +66,11 @@ mergeInto(LibraryManager.library, {
|
|||
} else if (FS.isFile(node.mode)) {
|
||||
node.node_ops = MEMFS.ops_table.file.node;
|
||||
node.stream_ops = MEMFS.ops_table.file.stream;
|
||||
node.contents = [];
|
||||
node.contentMode = MEMFS.CONTENT_FLEXIBLE;
|
||||
node.usedBytes = 0; // The actual number of bytes used in the typed array, as opposed to contents.buffer.byteLength which gives the whole capacity.
|
||||
// When the byte data of the file is populated, this will point to either a typed array, or a normal JS array. Typed arrays are preferred
|
||||
// for performance, and used by default. However, typed arrays are not resizable like normal JS arrays are, so there is a small disk size
|
||||
// penalty involved for appending file writes that continuously grow a file similar to std::vector capacity vs used -scheme.
|
||||
node.contents = null;
|
||||
} else if (FS.isLink(node.mode)) {
|
||||
node.node_ops = MEMFS.ops_table.link.node;
|
||||
node.stream_ops = MEMFS.ops_table.link.stream;
|
||||
|
@ -87,13 +85,88 @@ mergeInto(LibraryManager.library, {
|
|||
}
|
||||
return node;
|
||||
},
|
||||
ensureFlexible: function(node) {
|
||||
if (node.contentMode !== MEMFS.CONTENT_FLEXIBLE) {
|
||||
var contents = node.contents;
|
||||
node.contents = Array.prototype.slice.call(contents);
|
||||
node.contentMode = MEMFS.CONTENT_FLEXIBLE;
|
||||
|
||||
// Given a file node, returns its file data converted to a regular JS array. You should treat this as read-only.
|
||||
getFileDataAsRegularArray: function(node) {
|
||||
#if USE_TYPED_ARRAYS == 2
|
||||
if (node.contents && node.contents.subarray) {
|
||||
var arr = [];
|
||||
for (var i = 0; i < node.usedBytes; ++i) arr.push(node.contents[i]);
|
||||
return arr; // Returns a copy of the original data.
|
||||
}
|
||||
#endif
|
||||
return node.contents; // No-op, the file contents are already in a JS array. Return as-is.
|
||||
},
|
||||
|
||||
#if USE_TYPED_ARRAYS == 2
|
||||
// Given a file node, returns its file data converted to a typed array.
|
||||
getFileDataAsTypedArray: function(node) {
|
||||
if (node.contents && node.contents.subarray) return node.contents.subarray(0, node.usedBytes); // Make sure to not return excess unused bytes.
|
||||
return new Uint8Array(node.contents);
|
||||
},
|
||||
#endif
|
||||
|
||||
// Allocates a new backing store for the given node so that it can fit at least newSize amount of bytes.
|
||||
// May allocate more, to provide automatic geometric increase and amortized linear performance appending writes.
|
||||
// Never shrinks the storage.
|
||||
expandFileStorage: function(node, newCapacity) {
|
||||
#if USE_TYPED_ARRAYS == 2
|
||||
|
||||
#if !MEMFS_APPEND_TO_TYPED_ARRAYS
|
||||
// If we are asked to expand the size of a file that already exists, revert to using a standard JS array to store the file
|
||||
// instead of a typed array. This makes resizing the array more flexible because we can just .push() elements at the back to
|
||||
// increase the size.
|
||||
if (node.contents && node.contents.subarray && newCapacity > node.contents.length) {
|
||||
node.contents = MEMFS.getFileDataAsRegularArray(node);
|
||||
node.usedBytes = node.contents.length; // We might be writing to a lazy-loaded file which had overridden this property, so force-reset it.
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!node.contents || node.contents.subarray) { // Keep using a typed array if creating a new storage, or if old one was a typed array as well.
|
||||
var prevCapacity = node.contents ? node.contents.buffer.byteLength : 0;
|
||||
if (prevCapacity >= newCapacity) return; // No need to expand, the storage was already large enough.
|
||||
// Don't expand strictly to the given requested limit if it's only a very small increase, but instead geometrically grow capacity.
|
||||
// For small filesizes (<1MB), perform size*2 geometric increase, but for large sizes, do a much more conservative size*1.125 increase to
|
||||
// avoid overshooting the allocation cap by a very large margin.
|
||||
var CAPACITY_DOUBLING_MAX = 1024 * 1024;
|
||||
newCapacity = Math.max(newCapacity, (prevCapacity * (prevCapacity < CAPACITY_DOUBLING_MAX ? 2.0 : 1.125)) | 0);
|
||||
if (prevCapacity != 0) newCapacity = Math.max(newCapacity, 256); // At minimum allocate 256b for each file when expanding.
|
||||
var oldContents = node.contents;
|
||||
node.contents = new Uint8Array(newCapacity); // Allocate new storage.
|
||||
if (node.usedBytes > 0) node.contents.set(oldContents.subarray(0, node.usedBytes), 0); // Copy old data over to the new storage.
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
// Not using a typed array to back the file storage. Use a standard JS array instead.
|
||||
if (!node.contents && newCapacity > 0) node.contents = [];
|
||||
while (node.contents.length < newCapacity) node.contents.push(0);
|
||||
},
|
||||
|
||||
// Performs an exact resize of the backing file storage to the given size, if the size is not exactly this, the storage is fully reallocated.
|
||||
resizeFileStorage: function(node, newSize) {
|
||||
if (node.usedBytes == newSize) return;
|
||||
if (newSize == 0) {
|
||||
node.contents = null; // Fully decommit when requesting a resize to zero.
|
||||
node.usedBytes = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
#if USE_TYPED_ARRAYS == 2
|
||||
if (!node.contents || node.contents.subarray) { // Resize a typed array if that is being used as the backing store.
|
||||
var oldContents = node.contents;
|
||||
node.contents = new Uint8Array(new ArrayBuffer(newSize)); // Allocate new storage.
|
||||
node.contents.set(oldContents.subarray(0, Math.min(newSize, node.usedBytes))); // Copy old data over to the new storage.
|
||||
node.usedBytes = newSize;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
// Backing with a JS array.
|
||||
if (!node.contents) node.contents = [];
|
||||
if (node.contents.length > newSize) node.contents.length = newSize;
|
||||
else while (node.contents.length < newSize) node.contents.push(0);
|
||||
node.usedBytes = newSize;
|
||||
},
|
||||
|
||||
node_ops: {
|
||||
getattr: function(node) {
|
||||
var attr = {};
|
||||
|
@ -108,7 +181,7 @@ mergeInto(LibraryManager.library, {
|
|||
if (FS.isDir(node.mode)) {
|
||||
attr.size = 4096;
|
||||
} else if (FS.isFile(node.mode)) {
|
||||
attr.size = node.contents.length;
|
||||
attr.size = node.usedBytes;
|
||||
} else if (FS.isLink(node.mode)) {
|
||||
attr.size = node.link.length;
|
||||
} else {
|
||||
|
@ -131,10 +204,7 @@ mergeInto(LibraryManager.library, {
|
|||
node.timestamp = attr.timestamp;
|
||||
}
|
||||
if (attr.size !== undefined) {
|
||||
MEMFS.ensureFlexible(node);
|
||||
var contents = node.contents;
|
||||
if (attr.size < contents.length) contents.length = attr.size;
|
||||
else while (attr.size > contents.length) contents.push(0);
|
||||
MEMFS.resizeFileStorage(node, attr.size);
|
||||
}
|
||||
},
|
||||
lookup: function(parent, name) {
|
||||
|
@ -198,9 +268,8 @@ mergeInto(LibraryManager.library, {
|
|||
stream_ops: {
|
||||
read: function(stream, buffer, offset, length, position) {
|
||||
var contents = stream.node.contents;
|
||||
if (position >= contents.length)
|
||||
return 0;
|
||||
var size = Math.min(contents.length - position, length);
|
||||
if (position >= stream.node.usedBytes) return 0;
|
||||
var size = Math.min(stream.node.usedBytes - position, length);
|
||||
assert(size >= 0);
|
||||
#if USE_TYPED_ARRAYS == 2
|
||||
if (size > 8 && contents.subarray) { // non-trivial, and typed array
|
||||
|
@ -208,47 +277,56 @@ mergeInto(LibraryManager.library, {
|
|||
} else
|
||||
#endif
|
||||
{
|
||||
for (var i = 0; i < size; i++) {
|
||||
buffer[offset + i] = contents[position + i];
|
||||
}
|
||||
for (var i = 0; i < size; i++) buffer[offset + i] = contents[position + i];
|
||||
}
|
||||
return size;
|
||||
},
|
||||
|
||||
// Writes the byte range (buffer[offset], buffer[offset+length]) to offset 'position' into the file pointed by 'stream'
|
||||
write: function(stream, buffer, offset, length, position, canOwn) {
|
||||
if (!length) return 0;
|
||||
var node = stream.node;
|
||||
node.timestamp = Date.now();
|
||||
var contents = node.contents;
|
||||
|
||||
#if USE_TYPED_ARRAYS == 2
|
||||
if (length && contents.length === 0 && position === 0 && buffer.subarray) {
|
||||
// just replace it with the new data
|
||||
if (buffer.subarray && (!node.contents || node.contents.subarray)) { // This write is from a typed array to a typed array?
|
||||
if (canOwn) { // Can we just reuse the buffer we are given?
|
||||
#if ASSERTIONS
|
||||
assert(buffer.length);
|
||||
assert(position === 0, 'canOwn must imply no weird position inside the file');
|
||||
#endif
|
||||
if (canOwn && offset === 0) {
|
||||
node.contents = buffer; // this could be a subarray of Emscripten HEAP, or allocated from some other source.
|
||||
node.contentMode = (buffer.buffer === HEAP8.buffer) ? MEMFS.CONTENT_OWNING : MEMFS.CONTENT_FIXED;
|
||||
} else {
|
||||
node.contents = new Uint8Array(buffer.subarray(offset, offset+length));
|
||||
node.contentMode = MEMFS.CONTENT_FIXED;
|
||||
node.contents = buffer.subarray(offset, offset + length);
|
||||
node.usedBytes = length;
|
||||
return length;
|
||||
} else if (node.usedBytes === 0 && position === 0) { // If this is a simple first write to an empty file, do a fast set since we don't need to care about old data.
|
||||
node.contents = new Uint8Array(buffer.subarray(offset, offset + length));
|
||||
node.usedBytes = length;
|
||||
return length;
|
||||
} else if (position + length <= node.usedBytes) { // Writing to an already allocated and used subrange of the file?
|
||||
node.contents.set(buffer.subarray(offset, offset + length), position);
|
||||
return length;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
#endif
|
||||
MEMFS.ensureFlexible(node);
|
||||
var contents = node.contents;
|
||||
while (contents.length < position) contents.push(0);
|
||||
for (var i = 0; i < length; i++) {
|
||||
contents[position + i] = buffer[offset + i];
|
||||
}
|
||||
// Appending to an existing file and we need to reallocate, or source data did not come as a typed array.
|
||||
MEMFS.expandFileStorage(node, position+length);
|
||||
#if USE_TYPED_ARRAYS == 2
|
||||
if (node.contents.subarray && buffer.subarray) node.contents.set(buffer.subarray(offset, offset + length), position); // Use typed array write if available.
|
||||
else
|
||||
#endif
|
||||
for (var i = 0; i < length; i++) {
|
||||
node.contents[position + i] = buffer[offset + i]; // Or fall back to manual write if not.
|
||||
}
|
||||
node.usedBytes = Math.max(node.usedBytes, position+length);
|
||||
return length;
|
||||
},
|
||||
|
||||
llseek: function(stream, offset, whence) {
|
||||
var position = offset;
|
||||
if (whence === 1) { // SEEK_CUR.
|
||||
position += stream.position;
|
||||
} else if (whence === 2) { // SEEK_END.
|
||||
if (FS.isFile(stream.node.mode)) {
|
||||
position += stream.node.contents.length;
|
||||
position += stream.node.usedBytes;
|
||||
}
|
||||
}
|
||||
if (position < 0) {
|
||||
|
@ -259,10 +337,8 @@ mergeInto(LibraryManager.library, {
|
|||
return position;
|
||||
},
|
||||
allocate: function(stream, offset, length) {
|
||||
MEMFS.ensureFlexible(stream.node);
|
||||
var contents = stream.node.contents;
|
||||
var limit = offset + length;
|
||||
while (limit > contents.length) contents.push(0);
|
||||
MEMFS.expandFileStorage(stream.node, offset + length);
|
||||
stream.node.usedBytes = Math.max(stream.node.usedBytes, offset + length);
|
||||
},
|
||||
mmap: function(stream, buffer, offset, length, position, prot, flags) {
|
||||
if (!FS.isFile(stream.node.mode)) {
|
||||
|
@ -280,7 +356,7 @@ mergeInto(LibraryManager.library, {
|
|||
ptr = contents.byteOffset;
|
||||
} else {
|
||||
// Try to avoid unnecessary slices.
|
||||
if (position > 0 || position + length < contents.length) {
|
||||
if (position > 0 || position + length < stream.node.usedBytes) {
|
||||
if (contents.subarray) {
|
||||
contents = contents.subarray(position, position + length);
|
||||
} else {
|
||||
|
|
|
@ -84,6 +84,9 @@ var LibrarySDL = {
|
|||
|
||||
TOUCH_DEFAULT_ID: 0, // Our default deviceID for touch events (we get nothing from the browser)
|
||||
|
||||
eventHandler: null,
|
||||
eventHandlerContext: null,
|
||||
|
||||
keyCodes: { // DOM code ==> SDL code. See https://developer.mozilla.org/en/Document_Object_Model_%28DOM%29/KeyboardEvent and SDL_keycode.h
|
||||
// For keys that don't have unicode value, we map DOM codes with the corresponding scan codes + 1024 (using "| 1 << 10")
|
||||
16: 225 | 1<<10, // shift
|
||||
|
@ -304,7 +307,12 @@ var LibrarySDL = {
|
|||
{{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.pitch, 'width * bpp', 'i32') }}}; // assuming RGBA or indexed for now,
|
||||
// since that is what ImageData gives us in browsers
|
||||
{{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.pixels, 'buffer', 'void*') }}};
|
||||
{{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.clip_rect, '0', 'i32*') }}};
|
||||
|
||||
{{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.clip_rect+C_STRUCTS.SDL_Rect.x, '0', 'i32') }}};
|
||||
{{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.clip_rect+C_STRUCTS.SDL_Rect.y, '0', 'i32') }}};
|
||||
{{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.clip_rect+C_STRUCTS.SDL_Rect.w, 'Module["canvas"].width', 'i32') }}};
|
||||
{{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.clip_rect+C_STRUCTS.SDL_Rect.h, 'Module["canvas"].height', 'i32') }}};
|
||||
|
||||
{{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.refcount, '1', 'i32') }}};
|
||||
|
||||
{{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.format, cDefine('SDL_PIXELFORMAT_RGBA8888'), 'i32') }}};
|
||||
|
@ -699,6 +707,9 @@ var LibrarySDL = {
|
|||
Module.printErr('SDL event queue full, dropping events');
|
||||
SDL.events = SDL.events.slice(0, 10000);
|
||||
}
|
||||
// If we have a handler installed, this will push the events to the app
|
||||
// instead of the app polling for them.
|
||||
SDL.flushEventsToHandler();
|
||||
return;
|
||||
},
|
||||
|
||||
|
@ -750,6 +761,30 @@ var LibrarySDL = {
|
|||
}
|
||||
},
|
||||
|
||||
flushEventsToHandler: function() {
|
||||
if (!SDL.eventHandler) return;
|
||||
|
||||
// All SDLEvents take the same amount of memory
|
||||
var sdlEventPtr = allocate({{{ C_STRUCTS.SDL_KeyboardEvent.__size__ }}}, "i8", ALLOC_STACK);
|
||||
|
||||
while (SDL.pollEvent(sdlEventPtr)) {
|
||||
Runtime.dynCall('iii', SDL.eventHandler, [SDL.eventHandlerContext, sdlEventPtr]);
|
||||
}
|
||||
},
|
||||
|
||||
pollEvent: function(ptr) {
|
||||
if (SDL.initFlags & 0x200 && SDL.joystickEventState) {
|
||||
// If SDL_INIT_JOYSTICK was supplied AND the joystick system is configured
|
||||
// to automatically query for events, query for joystick events.
|
||||
SDL.queryJoysticks();
|
||||
}
|
||||
if (SDL.events.length === 0) return 0;
|
||||
if (ptr) {
|
||||
SDL.makeCEvent(SDL.events.shift(), ptr);
|
||||
}
|
||||
return 1;
|
||||
},
|
||||
|
||||
makeCEvent: function(event, ptr) {
|
||||
if (typeof event === 'number') {
|
||||
// This is a pointer to a native C event that was SDL_PushEvent'ed
|
||||
|
@ -1715,16 +1750,7 @@ var LibrarySDL = {
|
|||
},
|
||||
|
||||
SDL_PollEvent: function(ptr) {
|
||||
if (SDL.initFlags & 0x200 && SDL.joystickEventState) {
|
||||
// If SDL_INIT_JOYSTICK was supplied AND the joystick system is configured
|
||||
// to automatically query for events, query for joystick events.
|
||||
SDL.queryJoysticks();
|
||||
}
|
||||
if (SDL.events.length === 0) return 0;
|
||||
if (ptr) {
|
||||
SDL.makeCEvent(SDL.events.shift(), ptr);
|
||||
}
|
||||
return 1;
|
||||
return SDL.pollEvent(ptr);
|
||||
},
|
||||
|
||||
SDL_PushEvent: function(ptr) {
|
||||
|
@ -1764,6 +1790,13 @@ var LibrarySDL = {
|
|||
SDL.handleEvent(event);
|
||||
});
|
||||
},
|
||||
|
||||
// An Emscripten-specific extension to SDL: Some browser APIs require that they are called from within an event handler function.
|
||||
// Allow recording a callback that will be called for each received event.
|
||||
emscripten_SDL_SetEventHandler: function(handler, userdata) {
|
||||
SDL.eventHandler = handler;
|
||||
SDL.eventHandlerContext = userdata;
|
||||
},
|
||||
|
||||
SDL_SetColors: function(surf, colors, firstColor, nColors) {
|
||||
var surfData = SDL.surfaces[surf];
|
||||
|
@ -2607,7 +2640,7 @@ var LibrarySDL = {
|
|||
if (info && info.audio) {
|
||||
info.audio.pause();
|
||||
} else {
|
||||
Module.printErr('Mix_Pause: no sound found for channel: ' + channel);
|
||||
//Module.printErr('Mix_Pause: no sound found for channel: ' + channel);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -443,6 +443,31 @@ var LibraryManager = {
|
|||
}
|
||||
}
|
||||
|
||||
// apply synonyms. these are typically not speed-sensitive, and doing it this way makes it possible to not include hacks in the compiler
|
||||
// (and makes it simpler to switch between SDL verisons, fastcomp and non-fastcomp, etc.).
|
||||
var lib = LibraryManager.library;
|
||||
libloop: for (var x in lib) {
|
||||
if (x.lastIndexOf('__') > 0) continue; // ignore __deps, __*
|
||||
if (lib[x + '__asm']) continue; // ignore asm library functions, those need to be fully optimized
|
||||
if (typeof lib[x] === 'string') {
|
||||
var target = x;
|
||||
while (typeof lib[target] === 'string') {
|
||||
if (lib[target].indexOf('(') >= 0) continue libloop;
|
||||
target = lib[target];
|
||||
}
|
||||
if (typeof lib[target] === 'undefined' || typeof lib[target] === 'function') {
|
||||
if (target.indexOf('Math_') < 0) {
|
||||
lib[x] = new Function('return _' + target + '.apply(null, arguments)');
|
||||
if (!lib[x + '__deps']) lib[x + '__deps'] = [];
|
||||
lib[x + '__deps'].push(target);
|
||||
} else {
|
||||
lib[x] = new Function('return ' + target + '.apply(null, arguments)');
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// export code for CallHandlers.h
|
||||
printErr('============================');
|
||||
|
|
|
@ -323,6 +323,10 @@ var FS_LOG = 0; // Log all FS operations. This is especially helpful when you'r
|
|||
// so that you can create a virtual file system with all of the required files.
|
||||
var CASE_INSENSITIVE_FS = 0; // If set to nonzero, the provided virtual filesystem if treated case-insensitive, like
|
||||
// Windows and OSX do. If set to 0, the VFS is case-sensitive, like on Linux.
|
||||
var MEMFS_APPEND_TO_TYPED_ARRAYS = 0; // If set to nonzero, MEMFS will always utilize typed arrays as the backing store
|
||||
// for appending data to files. The default behavior is to use typed arrays for files
|
||||
// when the file size doesn't change after initial creation, and for files that do
|
||||
// change size, use normal JS arrays instead.
|
||||
|
||||
var USE_BSS = 1; // https://en.wikipedia.org/wiki/.bss
|
||||
// When enabled, 0-initialized globals are sorted to the end of the globals list,
|
||||
|
@ -474,6 +478,9 @@ var HEADLESS = 0; // If 1, will include shim code that tries to 'fake' a browser
|
|||
// very partial - it is hard to fake a whole browser! - so
|
||||
// keep your expectations low for this to work.
|
||||
|
||||
var DETERMINISTIC = 0; // If 1, we force Date.now(), Math.random, etc. to return deterministic
|
||||
// results. Good for comparing builds for debugging purposes (and nothing else)
|
||||
|
||||
var BENCHMARK = 0; // If 1, will just time how long main() takes to execute, and not
|
||||
// print out anything at all whatsoever. This is useful for benchmarking.
|
||||
|
||||
|
|
|
@ -615,6 +615,14 @@ extern DECLSPEC void SDLCALL SDL_DelEventWatch(SDL_EventFilter filter,
|
|||
extern DECLSPEC void SDLCALL SDL_FilterEvents(SDL_EventFilter filter,
|
||||
void *userdata);
|
||||
|
||||
/**
|
||||
* An Emscripten-specific extension to SDL: Some browser APIs require that they are called from within an event handler function.
|
||||
* Allow recording a callback that will be called for each received event. This is used in place of SDL_PollEvent.
|
||||
* Your application will be called whenever there are events available.
|
||||
*/
|
||||
extern DECLSPEC void SDLCALL emscripten_SDL_SetEventHandler(SDL_EventFilter handler,
|
||||
void *userdata);
|
||||
|
||||
/*@{*/
|
||||
#define SDL_QUERY -1
|
||||
#define SDL_IGNORE 0
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#if !__EMSCRIPTEN__
|
||||
#include <SDL/SDL.h> /* for SDL_Delay in async_call */
|
||||
#endif
|
||||
|
@ -515,6 +517,24 @@ int emscripten_get_compiler_setting(const char *name);
|
|||
*/
|
||||
void emscripten_debugger();
|
||||
|
||||
/*
|
||||
* Get preloaded image data and the size of the image.
|
||||
*
|
||||
* Returns pointer to loaded image or NULL.
|
||||
* width/height of image are written to w/h if data is valid.
|
||||
* Pointer should be free()'d
|
||||
*/
|
||||
char *emscripten_get_preloaded_image_data(const char *path, int *w, int *h);
|
||||
|
||||
/*
|
||||
* Get preloaded image data from a c FILE *.
|
||||
*
|
||||
* Returns pointer to loaded image or NULL.
|
||||
* width/height of image are written to w/h if data is valid.
|
||||
* Pointer should be free()'d
|
||||
*/
|
||||
char *emscripten_get_preloaded_image_data_from_FILE(FILE *file, int *w, int *h);
|
||||
|
||||
|
||||
/* ===================================== */
|
||||
/* Internal APIs. Be careful with these. */
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
You may place local includes that you require here and they will
|
||||
be found by the default compiler search path.
|
|
@ -0,0 +1,24 @@
|
|||
#include<assert.h>
|
||||
#include<stdio.h>
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
FILE *fp;
|
||||
int res;
|
||||
long len;
|
||||
|
||||
fp = fopen("testappend", "wb+");
|
||||
res = fwrite("1234567890", 10, 1, fp);
|
||||
fclose(fp);
|
||||
|
||||
fp = fopen("testappend", "ab+");
|
||||
res = fwrite("1234567890", 10, 1, fp);
|
||||
|
||||
fseek(fp, -7, SEEK_END);
|
||||
len = ftell(fp);
|
||||
assert(len == 13);
|
||||
fclose(fp);
|
||||
|
||||
puts("success");
|
||||
return 0;
|
||||
}
|
|
@ -376,6 +376,10 @@ void PullInfo(){
|
|||
extension = "GL_EXT_framebuffer_object";
|
||||
printf("'%s' extension is %s.\n", extension, glfwExtensionSupported(extension) ? "supported" : "not supported");
|
||||
|
||||
extension = "glBindBuffer";
|
||||
void* proc_addr = glfwGetProcAddress(extension);
|
||||
printf("'%s' extension proc address is %p.\n", extension, proc_addr);
|
||||
|
||||
printf("Sleeping 1 sec...\n");
|
||||
glfwSleep(1);
|
||||
printf("...Done.\n");
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <assert.h>
|
||||
#include <emscripten.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int testImage(SDL_Surface* screen, const char* fileName) {
|
||||
SDL_Surface *image = IMG_Load(fileName);
|
||||
|
@ -18,7 +19,16 @@ int testImage(SDL_Surface* screen, const char* fileName) {
|
|||
int result = image->w;
|
||||
|
||||
SDL_BlitSurface (image, NULL, screen, NULL);
|
||||
|
||||
int w, h;
|
||||
char *data = emscripten_get_preloaded_image_data(fileName, &w, &h);
|
||||
|
||||
assert(data);
|
||||
assert(w == image->w);
|
||||
assert(h == image->h);
|
||||
|
||||
SDL_FreeSurface (image);
|
||||
free(data);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -5,55 +5,68 @@
|
|||
|
||||
int result = 1;
|
||||
|
||||
int SDLCALL EventHandler(void *userdata, SDL_Event *event) {
|
||||
switch(event->type) {
|
||||
case SDL_KEYDOWN:
|
||||
break;
|
||||
case SDL_KEYUP:
|
||||
// don't handle the modifier key events
|
||||
if (event->key.keysym.sym == SDLK_LCTRL ||
|
||||
event->key.keysym.sym == SDLK_LSHIFT ||
|
||||
event->key.keysym.sym == SDLK_LALT) {
|
||||
return 0;
|
||||
}
|
||||
if ((event->key.keysym.mod & KMOD_LCTRL) || (event->key.keysym.mod & KMOD_RCTRL)) {
|
||||
result *= 2;
|
||||
}
|
||||
if ((event->key.keysym.mod & KMOD_LSHIFT) || (event->key.keysym.mod & KMOD_RSHIFT)) {
|
||||
result *= 3;
|
||||
}
|
||||
if ((event->key.keysym.mod & KMOD_LALT) || (event->key.keysym.mod & KMOD_RALT)) {
|
||||
result *= 5;
|
||||
}
|
||||
switch (event->key.keysym.sym) {
|
||||
case SDLK_RIGHT: printf("right\n"); result *= 7; break;
|
||||
case SDLK_LEFT: printf("left\n"); result *= 11; break;
|
||||
case SDLK_DOWN: printf("down\n"); result *= 13; break;
|
||||
case SDLK_UP: printf("up\n"); result *= 17; break;
|
||||
case SDLK_a: printf("a\n"); result *= 19; break;
|
||||
default: {
|
||||
if (event->key.keysym.scancode == SDL_SCANCODE_B) {
|
||||
printf("b scancode\n"); result *= 23; break;
|
||||
}
|
||||
printf("unknown key: sym %d scancode %d\n", event->key.keysym.sym, event->key.keysym.scancode);
|
||||
REPORT_RESULT();
|
||||
emscripten_run_script("throw 'done'"); // comment this out to leave event handling active. Use the following to log DOM keys:
|
||||
// addEventListener('keyup', function(event) { console.log(event->keyCode) }, true)
|
||||
}
|
||||
}
|
||||
break;
|
||||
default: /* Report an unhandled event */
|
||||
printf("I don't know what this event is (type=%d)!\n", event->type);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void one() {
|
||||
#ifndef TEST_EMSCRIPTEN_SDL_SETEVENTHANDLER
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
switch(event.type) {
|
||||
case SDL_KEYDOWN:
|
||||
break;
|
||||
case SDL_KEYUP:
|
||||
// don't handle the modifier key events
|
||||
if (event.key.keysym.sym == SDLK_LCTRL ||
|
||||
event.key.keysym.sym == SDLK_LSHIFT ||
|
||||
event.key.keysym.sym == SDLK_LALT) {
|
||||
return;
|
||||
}
|
||||
if ((event.key.keysym.mod & KMOD_LCTRL) || (event.key.keysym.mod & KMOD_RCTRL)) {
|
||||
result *= 2;
|
||||
}
|
||||
if ((event.key.keysym.mod & KMOD_LSHIFT) || (event.key.keysym.mod & KMOD_RSHIFT)) {
|
||||
result *= 3;
|
||||
}
|
||||
if ((event.key.keysym.mod & KMOD_LALT) || (event.key.keysym.mod & KMOD_RALT)) {
|
||||
result *= 5;
|
||||
}
|
||||
switch (event.key.keysym.sym) {
|
||||
case SDLK_RIGHT: printf("right\n"); result *= 7; break;
|
||||
case SDLK_LEFT: printf("left\n"); result *= 11; break;
|
||||
case SDLK_DOWN: printf("down\n"); result *= 13; break;
|
||||
case SDLK_UP: printf("up\n"); result *= 17; break;
|
||||
case SDLK_a: printf("a\n"); result *= 19; break;
|
||||
default: {
|
||||
if (event.key.keysym.scancode == SDL_SCANCODE_B) {
|
||||
printf("b scancode\n"); result *= 23; break;
|
||||
}
|
||||
printf("unknown key: sym %d scancode %d\n", event.key.keysym.sym, event.key.keysym.scancode);
|
||||
REPORT_RESULT();
|
||||
emscripten_run_script("throw 'done'"); // comment this out to leave event handling active. Use the following to log DOM keys:
|
||||
// addEventListener('keyup', function(event) { console.log(event.keyCode) }, true)
|
||||
}
|
||||
}
|
||||
break;
|
||||
default: /* Report an unhandled event */
|
||||
printf("I don't know what this event is!\n");
|
||||
}
|
||||
EventHandler(0, &event);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
SDL_Init(SDL_INIT_VIDEO);
|
||||
SDL_Surface *screen = SDL_SetVideoMode(600, 450, 32, SDL_HWSURFACE);
|
||||
|
||||
#ifdef TEST_EMSCRIPTEN_SDL_SETEVENTHANDLER
|
||||
emscripten_SDL_SetEventHandler(EventHandler, 0);
|
||||
#else
|
||||
if (argc == 1337) one(); // keep it alive
|
||||
#endif
|
||||
|
||||
emscripten_run_script("keydown(1250);keydown(38);keyup(38);keyup(1250);"); // alt, up
|
||||
emscripten_run_script("keydown(1248);keydown(1249);keydown(40);keyup(40);keyup(1249);keyup(1248);"); // ctrl, shift, down
|
||||
emscripten_run_script("keydown(37);keyup(37);"); // left
|
||||
|
@ -62,8 +75,6 @@ int main(int argc, char **argv) {
|
|||
emscripten_run_script("keydown(66);keyup(66);"); // b
|
||||
emscripten_run_script("keydown(100);keyup(100);"); // trigger the end
|
||||
|
||||
if (argc == 1337) one(); // keep it alive
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -750,35 +750,36 @@ window.close = function() {
|
|||
self.btest('sdl_canvas_alpha.c', reference='sdl_canvas_alpha.png', reference_slack=9)
|
||||
|
||||
def test_sdl_key(self):
|
||||
open(os.path.join(self.get_dir(), 'pre.js'), 'w').write('''
|
||||
Module.postRun = function() {
|
||||
function doOne() {
|
||||
Module._one();
|
||||
for defines in [[], ['-DTEST_EMSCRIPTEN_SDL_SETEVENTHANDLER']]:
|
||||
open(os.path.join(self.get_dir(), 'pre.js'), 'w').write('''
|
||||
Module.postRun = function() {
|
||||
function doOne() {
|
||||
Module._one();
|
||||
setTimeout(doOne, 1000/60);
|
||||
}
|
||||
setTimeout(doOne, 1000/60);
|
||||
}
|
||||
setTimeout(doOne, 1000/60);
|
||||
}
|
||||
|
||||
function keydown(c) {
|
||||
var event = document.createEvent("KeyboardEvent");
|
||||
event.initKeyEvent("keydown", true, true, window,
|
||||
0, 0, 0, 0,
|
||||
c, c);
|
||||
document.dispatchEvent(event);
|
||||
}
|
||||
function keydown(c) {
|
||||
var event = document.createEvent("KeyboardEvent");
|
||||
event.initKeyEvent("keydown", true, true, window,
|
||||
0, 0, 0, 0,
|
||||
c, c);
|
||||
document.dispatchEvent(event);
|
||||
}
|
||||
|
||||
function keyup(c) {
|
||||
var event = document.createEvent("KeyboardEvent");
|
||||
event.initKeyEvent("keyup", true, true, window,
|
||||
0, 0, 0, 0,
|
||||
c, c);
|
||||
document.dispatchEvent(event);
|
||||
}
|
||||
''')
|
||||
open(os.path.join(self.get_dir(), 'sdl_key.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_key.c')).read()))
|
||||
function keyup(c) {
|
||||
var event = document.createEvent("KeyboardEvent");
|
||||
event.initKeyEvent("keyup", true, true, window,
|
||||
0, 0, 0, 0,
|
||||
c, c);
|
||||
document.dispatchEvent(event);
|
||||
}
|
||||
''')
|
||||
open(os.path.join(self.get_dir(), 'sdl_key.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_key.c')).read()))
|
||||
|
||||
Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_key.c'), '-o', 'page.html', '--pre-js', 'pre.js', '-s', '''EXPORTED_FUNCTIONS=['_main', '_one']''', '-s', 'NO_EXIT_RUNTIME=1']).communicate()
|
||||
self.run_browser('page.html', '', '/report_result?223092870')
|
||||
Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_key.c'), '-o', 'page.html'] + defines + ['--pre-js', 'pre.js', '-s', '''EXPORTED_FUNCTIONS=['_main', '_one']''', '-s', 'NO_EXIT_RUNTIME=1']).communicate()
|
||||
self.run_browser('page.html', '', '/report_result?223092870')
|
||||
|
||||
def test_sdl_key_proxy(self):
|
||||
open(os.path.join(self.get_dir(), 'pre.js'), 'w').write('''
|
||||
|
@ -1098,9 +1099,10 @@ keydown(100);keyup(100); // trigger the end
|
|||
shutil.move('test.html', 'third.html')
|
||||
|
||||
def test_fs_idbfs_sync(self):
|
||||
secret = str(time.time())
|
||||
self.btest(path_from_root('tests', 'fs', 'test_idbfs_sync.c'), '1', force_c=True, args=['-DFIRST', '-DSECRET=\'' + secret + '\'', '-s', '''EXPORTED_FUNCTIONS=['_main', '_success']'''])
|
||||
self.btest(path_from_root('tests', 'fs', 'test_idbfs_sync.c'), '1', force_c=True, args=['-DSECRET=\'' + secret + '\'', '-s', '''EXPORTED_FUNCTIONS=['_main', '_success']'''])
|
||||
for mode in [[], ['-s', 'MEMFS_APPEND_TO_TYPED_ARRAYS=1']]:
|
||||
secret = str(time.time())
|
||||
self.btest(path_from_root('tests', 'fs', 'test_idbfs_sync.c'), '1', force_c=True, args=mode + ['-DFIRST', '-DSECRET=\'' + secret + '\'', '-s', '''EXPORTED_FUNCTIONS=['_main', '_success']'''])
|
||||
self.btest(path_from_root('tests', 'fs', 'test_idbfs_sync.c'), '1', force_c=True, args=mode + ['-DSECRET=\'' + secret + '\'', '-s', '''EXPORTED_FUNCTIONS=['_main', '_success']'''])
|
||||
|
||||
def test_sdl_pumpevents(self):
|
||||
# key events should be detected using SDL_PumpEvents
|
||||
|
|
|
@ -4030,11 +4030,14 @@ def process(filename):
|
|||
src = open(path_from_root('tests', 'files.cpp'), 'r').read()
|
||||
|
||||
mem_file = 'src.cpp.o.js.mem'
|
||||
try_delete(mem_file)
|
||||
self.do_run(src, ('size: 7\ndata: 100,-56,50,25,10,77,123\nloop: 100 -56 50 25 10 77 123 \ninput:hi there!\ntexto\n$\n5 : 10,30,20,11,88\nother=some data.\nseeked=me da.\nseeked=ata.\nseeked=ta.\nfscanfed: 10 - hello\nok.\ntexte\n', 'size: 7\ndata: 100,-56,50,25,10,77,123\nloop: 100 -56 50 25 10 77 123 \ninput:hi there!\ntexto\ntexte\n$\n5 : 10,30,20,11,88\nother=some data.\nseeked=me da.\nseeked=ata.\nseeked=ta.\nfscanfed: 10 - hello\nok.\n'),
|
||||
post_build=post, extra_emscripten_args=['-H', 'libc/fcntl.h'])
|
||||
if self.emcc_args and '--memory-init-file' in self.emcc_args:
|
||||
assert os.path.exists(mem_file)
|
||||
orig_args = self.emcc_args
|
||||
for mode in [[], ['-s', 'MEMFS_APPEND_TO_TYPED_ARRAYS=1']]:
|
||||
self.emcc_args = orig_args + mode
|
||||
try_delete(mem_file)
|
||||
self.do_run(src, ('size: 7\ndata: 100,-56,50,25,10,77,123\nloop: 100 -56 50 25 10 77 123 \ninput:hi there!\ntexto\n$\n5 : 10,30,20,11,88\nother=some data.\nseeked=me da.\nseeked=ata.\nseeked=ta.\nfscanfed: 10 - hello\nok.\ntexte\n', 'size: 7\ndata: 100,-56,50,25,10,77,123\nloop: 100 -56 50 25 10 77 123 \ninput:hi there!\ntexto\ntexte\n$\n5 : 10,30,20,11,88\nother=some data.\nseeked=me da.\nseeked=ata.\nseeked=ta.\nfscanfed: 10 - hello\nok.\n'),
|
||||
post_build=post, extra_emscripten_args=['-H', 'libc/fcntl.h'])
|
||||
if self.emcc_args and '--memory-init-file' in self.emcc_args:
|
||||
assert os.path.exists(mem_file)
|
||||
|
||||
def test_files_m(self):
|
||||
# Test for Module.stdin etc.
|
||||
|
@ -4077,7 +4080,10 @@ def process(filename):
|
|||
test_path = path_from_root('tests', 'core', 'test_fwrite_0')
|
||||
src, output = (test_path + s for s in ('.in', '.out'))
|
||||
|
||||
self.do_run_from_file(src, output)
|
||||
orig_args = self.emcc_args if self.emcc_args else []
|
||||
for mode in [[], ['-s', 'MEMFS_APPEND_TO_TYPED_ARRAYS=1']]:
|
||||
self.emcc_args = orig_args + mode
|
||||
self.do_run_from_file(src, output)
|
||||
|
||||
def test_fgetc_ungetc(self):
|
||||
src = open(path_from_root('tests', 'stdio', 'test_fgetc_ungetc.c'), 'r').read()
|
||||
|
@ -4275,7 +4281,10 @@ def process(filename):
|
|||
if self.emcc_args is None: return self.skip('requires libcxx')
|
||||
test_path = path_from_root('tests', 'core', 'test_wprintf')
|
||||
src, output = (test_path + s for s in ('.c', '.out'))
|
||||
self.do_run_from_file(src, output)
|
||||
orig_args = self.emcc_args
|
||||
for mode in [[], ['-s', 'MEMFS_APPEND_TO_TYPED_ARRAYS=1']]:
|
||||
self.emcc_args = orig_args + mode
|
||||
self.do_run_from_file(src, output)
|
||||
|
||||
def test_direct_string_constant_usage(self):
|
||||
if self.emcc_args is None: return self.skip('requires libcxx')
|
||||
|
@ -4339,6 +4348,11 @@ def process(filename):
|
|||
out = path_from_root('tests', 'fs', 'test_writeFile.out')
|
||||
self.do_run_from_file(src, out)
|
||||
|
||||
def test_fs_append(self):
|
||||
if self.emcc_args is None: return self.skip('requires emcc')
|
||||
src = open(path_from_root('tests', 'fs', 'test_append.c'), 'r').read()
|
||||
self.do_run(src, 'success', force_c=True)
|
||||
|
||||
def test_unistd_access(self):
|
||||
self.clear()
|
||||
if not self.is_emscripten_abi(): return self.skip('asmjs-unknown-emscripten needed for inline js')
|
||||
|
@ -4643,6 +4657,39 @@ PORT: 3979
|
|||
'''
|
||||
self.do_run(src, 'value\narray_item1\narray_item2\narray_item3\n3\n3.00\n2.20\nJansson: Node with ID `0` not found. Context has `10` nodes.\n0\nJansson: No JSON context.\njansson!')
|
||||
|
||||
def test_js_libraries(self):
|
||||
if self.emcc_args == None: return self.skip('needs emcc')
|
||||
|
||||
open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write('''
|
||||
#include <stdio.h>
|
||||
extern "C" {
|
||||
extern void printey();
|
||||
extern int calcey(int x, int y);
|
||||
}
|
||||
int main() {
|
||||
printey();
|
||||
printf("*%d*\\n", calcey(10, 22));
|
||||
return 0;
|
||||
}
|
||||
''')
|
||||
open(os.path.join(self.get_dir(), 'mylib1.js'), 'w').write('''
|
||||
mergeInto(LibraryManager.library, {
|
||||
printey: function() {
|
||||
Module.print('hello from lib!');
|
||||
}
|
||||
});
|
||||
''')
|
||||
open(os.path.join(self.get_dir(), 'mylib2.js'), 'w').write('''
|
||||
mergeInto(LibraryManager.library, {
|
||||
calcey: function(x, y) {
|
||||
return x + y;
|
||||
}
|
||||
});
|
||||
''')
|
||||
|
||||
self.emcc_args += ['--js-library', os.path.join(self.get_dir(), 'mylib1.js'), '--js-library', os.path.join(self.get_dir(), 'mylib2.js')]
|
||||
self.do_run(open(os.path.join(self.get_dir(), 'main.cpp'), 'r').read(), 'hello from lib!\n*32*\n')
|
||||
|
||||
def test_constglobalunion(self):
|
||||
if self.emcc_args is None: return self.skip('needs emcc')
|
||||
self.emcc_args += ['-s', 'EXPORT_ALL=1']
|
||||
|
@ -5117,7 +5164,7 @@ def process(filename):
|
|||
\'\'\'
|
||||
FS.createDataFile('/', 'paper.pdf', eval(Module.read('paper.pdf.js')), true, false);
|
||||
Module.callMain(Module.arguments);
|
||||
Module.print("Data: " + JSON.stringify(FS.root.contents['filename-1.ppm'].contents.map(function(x) { return unSign(x, 8) })));
|
||||
Module.print("Data: " + JSON.stringify(MEMFS.getFileDataAsRegularArray(FS.root.contents['filename-1.ppm']).map(function(x) { return unSign(x, 8) })));
|
||||
\'\'\'
|
||||
)
|
||||
src.close()
|
||||
|
@ -5167,7 +5214,7 @@ def process(filename):
|
|||
))
|
||||
).replace(
|
||||
'// {{POST_RUN_ADDITIONS}}',
|
||||
"Module.print('Data: ' + JSON.stringify(FS.analyzePath('image.raw').object.contents));"
|
||||
"Module.print('Data: ' + JSON.stringify(MEMFS.getFileDataAsRegularArray(FS.analyzePath('image.raw').object)));"
|
||||
)
|
||||
open(filename, 'w').write(src)
|
||||
'''
|
||||
|
@ -6386,10 +6433,15 @@ def process(filename):
|
|||
if (i < 10) throw i; // line 5
|
||||
}
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
int main() {
|
||||
std::string x = "ok"; // add libc++ stuff to make this big, test for #2410
|
||||
int i;
|
||||
scanf("%d", &i);
|
||||
foo(i);
|
||||
std::cout << x << std::endl;
|
||||
return 0;
|
||||
}
|
||||
'''
|
||||
|
|
|
@ -1306,38 +1306,6 @@ This pointer might make sense in another type signature: i: 0
|
|||
|
||||
self.assertContained('TestA\nTestB\nTestA\n', run_js('main.js', engine=SPIDERMONKEY_ENGINE))
|
||||
|
||||
def test_js_libraries(self):
|
||||
open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write('''
|
||||
#include <stdio.h>
|
||||
extern "C" {
|
||||
extern void printey();
|
||||
extern int calcey(int x, int y);
|
||||
}
|
||||
int main() {
|
||||
printey();
|
||||
printf("*%d*\\n", calcey(10, 22));
|
||||
return 0;
|
||||
}
|
||||
''')
|
||||
open(os.path.join(self.get_dir(), 'mylib1.js'), 'w').write('''
|
||||
mergeInto(LibraryManager.library, {
|
||||
printey: function() {
|
||||
Module.print('hello from lib!');
|
||||
}
|
||||
});
|
||||
''')
|
||||
open(os.path.join(self.get_dir(), 'mylib2.js'), 'w').write('''
|
||||
mergeInto(LibraryManager.library, {
|
||||
calcey: function(x, y) {
|
||||
return x + y;
|
||||
}
|
||||
});
|
||||
''')
|
||||
|
||||
Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--js-library', os.path.join(self.get_dir(), 'mylib1.js'),
|
||||
'--js-library', os.path.join(self.get_dir(), 'mylib2.js')]).communicate()
|
||||
self.assertContained('hello from lib!\n*32*\n', run_js(os.path.join(self.get_dir(), 'a.out.js')))
|
||||
|
||||
def test_identical_basenames(self):
|
||||
# Issue 287: files in different dirs but with the same basename get confused as the same,
|
||||
# causing multiply defined symbol errors
|
||||
|
@ -2901,4 +2869,26 @@ int main(int argc, char **argv) {
|
|||
assert 'eval.' not in src
|
||||
assert 'new Function' not in src
|
||||
|
||||
def test_init_file_at_offset(self):
|
||||
open('src.cpp', 'w').write(r'''
|
||||
#include <stdio.h>
|
||||
int main() {
|
||||
int data = 0x12345678;
|
||||
FILE *f = fopen("test.dat", "wb");
|
||||
fseek(f, 100, SEEK_CUR);
|
||||
fwrite(&data, 4, 1, f);
|
||||
fclose(f);
|
||||
|
||||
int data2;
|
||||
f = fopen("test.dat", "rb");
|
||||
fread(&data2, 4, 1, f); // should read 0s, not that int we wrote at an offset
|
||||
printf("read: %d\n", data2);
|
||||
fseek(f, 0, SEEK_END);
|
||||
int size = ftell(f); // should be 104, not 4
|
||||
fclose(f);
|
||||
printf("file size is %d\n", size);
|
||||
}
|
||||
''')
|
||||
Popen([PYTHON, EMCC, 'src.cpp']).communicate()
|
||||
self.assertContained('read: 0\nfile size is 104\n', run_js('a.out.js'))
|
||||
|
||||
|
|
602
tools/ffdb.py
602
tools/ffdb.py
|
@ -1,7 +1,14 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import socket, json, sys, uuid, datetime, time, logging, cgi, zipfile, os, tempfile, atexit, subprocess
|
||||
import socket, json, sys, uuid, datetime, time, logging, cgi, zipfile, os, tempfile, atexit, subprocess, re, base64, struct, imghdr
|
||||
|
||||
WINDOWS = sys.platform == 'win32'
|
||||
if WINDOWS:
|
||||
import ctypes
|
||||
stdout_handle = ctypes.windll.kernel32.GetStdHandle(-11)
|
||||
|
||||
LOG_FORMAT = 'short' # Either 'bare', 'short', or 'long'
|
||||
ADB = 'adb' # Path to the adb executable
|
||||
LOG_VERBOSE = False # Verbose printing enabled with --verbose
|
||||
HOST = 'localhost' # The remote host to connect to the B2G device
|
||||
PORT = 6000 # The port on the host on which the B2G device listens on
|
||||
|
@ -18,7 +25,13 @@ def sizeof_fmt(num):
|
|||
return "%3.1f%s" % (num, 'TB')
|
||||
|
||||
def zipdir(path, zipfilename):
|
||||
zipf = zipfile.ZipFile(zipfilename, 'w')
|
||||
try:
|
||||
import zlib
|
||||
zip_mode = zipfile.ZIP_DEFLATED
|
||||
except:
|
||||
zip_mode = zipfile.ZIP_STORED
|
||||
|
||||
zipf = zipfile.ZipFile(zipfilename, 'w', zip_mode)
|
||||
files_to_compress = []
|
||||
for root, dirs, files in os.walk(path):
|
||||
for file in files:
|
||||
|
@ -29,9 +42,10 @@ def zipdir(path, zipfilename):
|
|||
(root, file) = tuple
|
||||
filename = os.path.join(root, file)
|
||||
filesize = os.path.getsize(filename)
|
||||
print 'Compressing ' + str(n) + '/' + str(len(files_to_compress)) + ': "' + os.path.relpath(filename, path) + '" (' + sizeof_fmt(filesize) + ')...'
|
||||
path_in_archive = os.path.relpath(filename, path)
|
||||
print 'Compressing ' + str(n) + '/' + str(len(files_to_compress)) + ': "' + path_in_archive + '" (' + sizeof_fmt(filesize) + ')...'
|
||||
n += 1
|
||||
zipf.write(os.path.join(root, file))
|
||||
zipf.write(os.path.join(root, file), path_in_archive)
|
||||
zipf.close()
|
||||
print 'Done. '
|
||||
|
||||
|
@ -46,30 +60,50 @@ def format_html(msg):
|
|||
# Prints a verbose log message to stdout channel. Only shown if run with --verbose.
|
||||
def logv(msg):
|
||||
if LOG_VERBOSE:
|
||||
sys.stdout.write(format_html(msg))
|
||||
sys.stdout.write(format_html(msg) + '\n')
|
||||
sys.stdout.flush()
|
||||
|
||||
# Reads data from the socket, and tries to parse what we have got so far as a JSON message.
|
||||
# The messages are of form "bytelength:{jsondict}", where bytelength tells how many bytes
|
||||
# there are in the data that comes after the colon.
|
||||
# Returns a JSON dictionary of the received message.
|
||||
def read_b2g_response():
|
||||
def read_b2g_response(print_errors_to_console = True):
|
||||
global read_queue, b2g_socket
|
||||
read_queue += b2g_socket.recv(65536*2)
|
||||
while ':' in read_queue:
|
||||
semicolon = read_queue.index(':')
|
||||
payload_len = int(read_queue[:semicolon])
|
||||
payload = ''
|
||||
while True:
|
||||
semicolon = float('Inf')
|
||||
payload_len = float('Inf')
|
||||
try:
|
||||
semicolon = read_queue.index(':')
|
||||
payload_len = int(read_queue[:semicolon])
|
||||
except:
|
||||
pass
|
||||
if semicolon+1+payload_len > len(read_queue):
|
||||
read_queue += b2g_socket.recv(65536*2)
|
||||
try:
|
||||
read_queue += b2g_socket.recv(4096)
|
||||
except socket.timeout, e:
|
||||
pass # We simulate blocking sockets with looping over reads that time out, since on Windows, the user cannot press Ctrl-C to break on blocking sockets.
|
||||
except Exception, e:
|
||||
if e[0] == 57: # Socket is not connected
|
||||
print 'Error! Failed to receive data from the device: socket is not connected!'
|
||||
sys.exit(1)
|
||||
else:
|
||||
print 'Got exception ' + str(e)
|
||||
raise
|
||||
continue
|
||||
payload = read_queue[semicolon+1:semicolon+1+payload_len]
|
||||
read_queue = read_queue[semicolon+1+payload_len:]
|
||||
logv('Read a message of size ' + str(payload_len) + 'b from socket.')
|
||||
payload = json.loads(payload)
|
||||
# Log received errors immediately to console
|
||||
if print_errors_to_console and 'error' in payload:
|
||||
print >> sys.stderr, 'Received error "' + payload['error'] + '"! Reason: ' + payload['message']
|
||||
else:
|
||||
break
|
||||
return payload
|
||||
|
||||
# Sends a command to the B2G device and waits for the response and returns it as a JSON dict.
|
||||
def send_b2g_cmd(to, cmd, data = {}):
|
||||
def send_b2g_cmd(to, cmd, data = {}, print_errors_to_console = True):
|
||||
global b2g_socket
|
||||
msg = { 'to': to, 'type': cmd}
|
||||
msg = dict(msg.items() + data.items())
|
||||
|
@ -78,7 +112,7 @@ def send_b2g_cmd(to, cmd, data = {}):
|
|||
msg = str(len(msg))+':'+msg
|
||||
logv('Sending cmd:' + cmd + ' to:' + to)
|
||||
b2g_socket.sendall(msg)
|
||||
return read_b2g_response()
|
||||
return read_b2g_response(print_errors_to_console)
|
||||
|
||||
def escape_bytes(b):
|
||||
return str(b)
|
||||
|
@ -102,7 +136,16 @@ def send_b2g_data_chunk(to, data_blob):
|
|||
i += 1
|
||||
message = '{"to":"'+to+'","type":"chunk","chunk":"' + ''.join(byte_str) + '"}'
|
||||
message = str(len(message)) + ':' + message
|
||||
logv('{"to":"'+to+'","type":"chunk","chunk":"<data>"}')
|
||||
b2g_socket.sendall(message)
|
||||
return read_b2g_response()
|
||||
|
||||
def send_b2g_bulk_data(to, data_blob):
|
||||
message = 'bulk ' + to + ' stream ' + str(len(data_blob)) + ':'
|
||||
logv(message)
|
||||
b2g_socket.sendall(message)
|
||||
b2g_socket.sendall(data_blob)
|
||||
# It seems that B2G doesn't send any response JSON back after a bulk transfer is finished, so no read_b2g_response() here.
|
||||
|
||||
# Queries the device for a list of all installed apps.
|
||||
def b2g_get_appslist():
|
||||
|
@ -130,8 +173,303 @@ def print_applist(applist, running_app_manifests, print_removable):
|
|||
num_printed += 1
|
||||
return num_printed
|
||||
|
||||
def adb_devices():
|
||||
try:
|
||||
devices = subprocess.check_output([ADB, 'devices'])
|
||||
devices = devices.strip().split('\n')[1:]
|
||||
devices = map(lambda x: x.strip().split('\t'), devices)
|
||||
return devices
|
||||
except Exception, e:
|
||||
return []
|
||||
|
||||
def b2g_get_prefs_filename():
|
||||
return subprocess.check_output([ADB, 'shell', 'echo', '-n', '/data/b2g/mozilla/*.default/prefs.js'])
|
||||
|
||||
def b2g_get_prefs_data():
|
||||
return subprocess.check_output([ADB, 'shell', 'cat', '/data/b2g/mozilla/*.default/prefs.js'])
|
||||
|
||||
def b2g_get_pref(sub):
|
||||
prefs_data = b2g_get_prefs_data().split('\n')
|
||||
# Filter to find all prefs that have the substring 'sub' in them.
|
||||
r = re.compile('user_pref\w*\(\w*"([^"]*)"\w*,\w*([^\)]*)')
|
||||
for line in prefs_data:
|
||||
m = r.match(line)
|
||||
if m and (sub is None or sub in m.group(1)):
|
||||
print m.group(1) + ': ' + m.group(2).strip()
|
||||
|
||||
def b2g_set_pref(pref, value):
|
||||
prefs_data = b2g_get_prefs_data().split('\n')
|
||||
# Remove any old value of this pref.
|
||||
r = re.compile('user_pref\w*\(\w*"([^"]*)"\w*,\w*([^\)]*)')
|
||||
new_prefs_data = []
|
||||
for line in prefs_data:
|
||||
m = r.match(line)
|
||||
if not m or m.group(1) != pref:
|
||||
new_prefs_data += [line]
|
||||
|
||||
if value != None:
|
||||
print 'Setting pref "' + pref + '" = ' + value
|
||||
new_prefs_data += ['user_pref("' + pref + '", ' + value + ');']
|
||||
else:
|
||||
print 'Unsetting pref "' + pref + '"'
|
||||
(oshandle, tempfilename) = tempfile.mkstemp(suffix='.js', prefix='ffdb_temp_')
|
||||
os.write(oshandle, '\n'.join(new_prefs_data));
|
||||
|
||||
# Write the new pref
|
||||
subprocess.check_output([ADB, 'shell', 'stop', 'b2g'])
|
||||
subprocess.check_output([ADB, 'push', tempfilename, b2g_get_prefs_filename()])
|
||||
subprocess.check_output([ADB, 'shell', 'start', 'b2g'])
|
||||
print 'Rebooting phone...'
|
||||
|
||||
def delete_temp_file():
|
||||
os.remove(tempfilename)
|
||||
atexit.register(delete_temp_file)
|
||||
|
||||
def get_packaged_app_manifest(target_app_path):
|
||||
if os.path.isdir(target_app_path):
|
||||
manifest_file = os.path.join(target_app_path, 'manifest.webapp')
|
||||
if not os.path.isfile(manifest_file):
|
||||
print "Error: Failed to find FFOS packaged app manifest file '" + manifest_file + "'! That directory does not contain a packaged app?"
|
||||
sys.exit(1)
|
||||
return json.loads(open(manifest_file, 'r').read())
|
||||
elif target_app_path.endswith('.zip') and os.path.isfile(target_app_path):
|
||||
try:
|
||||
z = zipfile.ZipFile(target_app_path, "r")
|
||||
bytes = z.read('manifest.webapp')
|
||||
except Exception, e:
|
||||
print "Error: Failed to read FFOS packaged app manifest file 'manifest.webapp' in zip file '" + target_app_path + "'! Error: " + str(e)
|
||||
sys.exit(1)
|
||||
return None
|
||||
return json.loads(str(bytes))
|
||||
else:
|
||||
print "Error: Path '" + target_app_path + "' is neither a directory or a .zip file to represent the location of a FFOS packaged app!"
|
||||
sys.exit(1)
|
||||
return None
|
||||
|
||||
def b2g_install(target_app_path):
|
||||
if os.path.isdir(target_app_path):
|
||||
print 'Zipping up the contents of directory "' + target_app_path + '"...'
|
||||
(oshandle, tempzip) = tempfile.mkstemp(suffix='.zip', prefix='ffdb_temp_')
|
||||
zipdir(target_app_path, tempzip)
|
||||
target_app_path = tempzip
|
||||
# Remember to delete the temporary package after we quit.
|
||||
def delete_temp_file():
|
||||
os.remove(tempzip)
|
||||
atexit.register(delete_temp_file)
|
||||
|
||||
print 'Uploading application package "' + target_app_path + '"...'
|
||||
print 'Size of compressed package: ' + sizeof_fmt(os.path.getsize(target_app_path)) + '.'
|
||||
app_file = open(target_app_path, 'rb')
|
||||
data = app_file.read()
|
||||
file_size = len(data)
|
||||
|
||||
uploadResponse = send_b2g_cmd(webappsActorName, 'uploadPackage', { 'bulk': 'true'}, print_errors_to_console = False) # This may fail if on old device.
|
||||
start_time = time.time()
|
||||
if 'actor' in uploadResponse and 'BulkActor' in uploadResponse['actor']: # New B2G 2.0 hotness: binary data transfer
|
||||
packageUploadActor = uploadResponse['actor']
|
||||
send_b2g_bulk_data(packageUploadActor, data)
|
||||
else: # Old B2G 1.4 and older, serialize binary data in JSON text strings (SLOW!)
|
||||
print 'Bulk upload is not supported, uploading binary data with old slow format. Consider flashing your device to FFOS 2.0 or newer to enjoy faster upload speeds.'
|
||||
uploadResponse = send_b2g_cmd(webappsActorName, 'uploadPackage')
|
||||
packageUploadActor = uploadResponse['actor']
|
||||
chunk_size = 4*1024*1024
|
||||
i = 0
|
||||
while i < file_size:
|
||||
chunk = data[i:i+chunk_size]
|
||||
|
||||
send_b2g_data_chunk(packageUploadActor, chunk)
|
||||
i += chunk_size
|
||||
bytes_uploaded = min(i, file_size)
|
||||
cur_time = time.time()
|
||||
secs_elapsed = cur_time - start_time
|
||||
percentage_done = bytes_uploaded * 1.0 / file_size
|
||||
total_time = secs_elapsed / percentage_done
|
||||
time_left = total_time - secs_elapsed
|
||||
print sizeof_fmt(bytes_uploaded) + " uploaded, {:5.1f} % done.".format(percentage_done*100.0) + ' Elapsed: ' + str(int(secs_elapsed)) + ' seconds. Time left: ' + str(datetime.timedelta(seconds=int(time_left))) + '. Data rate: {:5.2f} KB/second.'.format(bytes_uploaded / 1024.0 / secs_elapsed)
|
||||
|
||||
app_local_id = str(uuid.uuid4())
|
||||
reply = send_b2g_cmd(webappsActorName, 'install', { 'appId': app_local_id, 'upload': packageUploadActor })
|
||||
cur_time = time.time()
|
||||
secs_elapsed = cur_time - start_time
|
||||
print 'Upload of ' + sizeof_fmt(file_size) + ' finished. Total time elapsed: ' + str(int(secs_elapsed)) + ' seconds. Data rate: {:5.2f} KB/second.'.format(file_size / 1024.0 / secs_elapsed)
|
||||
if not 'appId' in reply:
|
||||
print 'Error: Application install failed! ' + str(reply)
|
||||
sys.exit()
|
||||
return reply['appId']
|
||||
|
||||
def b2g_app_command(app_command, app_name, print_errors_to_console = True):
|
||||
apps = b2g_get_appslist()
|
||||
for app in apps:
|
||||
if str(app['localId']) == app_name or app['name'] == app_name or app['manifestURL'] == app_name or app['id'] == app_name:
|
||||
send_b2g_cmd(webappsActorName, app_command, { 'manifestURL': app['manifestURL'] })
|
||||
return 0
|
||||
if print_errors_to_console:
|
||||
print 'Error! Application "' + app_name + '" was not found! Use the \'list\' command to find installed applications.'
|
||||
return 1
|
||||
|
||||
def b2g_memory(app_name):
|
||||
apps = b2g_get_appslist()
|
||||
appActor = ''
|
||||
for app in apps:
|
||||
if str(app['localId']) == app_name or app['name'] == app_name or app['manifestURL'] == app_name or app['id'] == app_name:
|
||||
appActor = send_b2g_cmd(webappsActorName, 'getAppActor', { 'manifestURL': app['manifestURL'] })
|
||||
break
|
||||
if 'actor' in appActor:
|
||||
memoryActor = appActor['actor']['memoryActor']
|
||||
measure = send_b2g_cmd(memoryActor, 'measure')
|
||||
for k,v in measure.items():
|
||||
if k != 'from':
|
||||
if k in ['otherSize', 'jsStringsSize', 'jsObjectsSize', 'styleSize', 'jsOtherSize', 'domSize', 'total']: # These are formatted in bytes
|
||||
print k + ': ' + sizeof_fmt(v)
|
||||
else:
|
||||
print k + ': ' + str(v)
|
||||
|
||||
def b2g_log(app_name, clear=False):
|
||||
global LOG_FORMAT
|
||||
apps = b2g_get_appslist()
|
||||
appActor = ''
|
||||
for app in apps:
|
||||
if str(app['localId']) == app_name or app['name'] == app_name or app['manifestURL'] == app_name or app['id'] == app_name:
|
||||
appActor = send_b2g_cmd(webappsActorName, 'getAppActor', { 'manifestURL': app['manifestURL'] })
|
||||
break
|
||||
if 'actor' in appActor:
|
||||
consoleActor = appActor['actor']['consoleActor']
|
||||
|
||||
if clear:
|
||||
send_b2g_cmd(consoleActor, 'clearMessagesCache')
|
||||
print 'Cleared message log.'
|
||||
return 0
|
||||
|
||||
msgs = send_b2g_cmd(consoleActor, 'startListeners', { 'listeners': ['PageError','ConsoleAPI','NetworkActivity','FileActivity'] })
|
||||
|
||||
if WINDOWS:
|
||||
WARNING = 14 # FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY
|
||||
FAIL = 12 # FOREGROUND_RED | FOREGROUND_INTENSITY
|
||||
INFO = 7 # FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
|
||||
ENDC = ''
|
||||
BOLD = ''
|
||||
else:
|
||||
WARNING = '\033[93m'
|
||||
FAIL = '\033[91m'
|
||||
INFO = ENDC = '\033[0m'
|
||||
BOLD = "\033[1m"
|
||||
|
||||
def set_color(string, color):
|
||||
if WINDOWS:
|
||||
ctypes.windll.kernel32.SetConsoleTextAttribute(stdout_handle, color)
|
||||
return string
|
||||
else:
|
||||
return color + string + ENDC
|
||||
|
||||
def reset_color():
|
||||
if WINDOWS:
|
||||
ctypes.windll.kernel32.SetConsoleTextAttribute(stdout_handle, INFO)
|
||||
|
||||
def log_b2g_message(msg):
|
||||
msgs = []
|
||||
if 'type' in msg and msg['type'] == 'consoleAPICall':
|
||||
msgs = [msg['message']]
|
||||
elif 'messages' in msg:
|
||||
msgs = msg['messages']
|
||||
|
||||
for m in msgs:
|
||||
args = m['arguments']
|
||||
|
||||
for arg in args:
|
||||
if LOG_FORMAT == 'long':
|
||||
text = str(m['functionName']) + '@' + str(m['filename']) + ':' + str(m['lineNumber']) + ': ' + str(arg)
|
||||
elif LOG_FORMAT == 'bare':
|
||||
text = str(arg)
|
||||
else: # Default to 'short'
|
||||
text = str(m['functionName']) + '@' + os.path.basename(str(m['filename'])) + ':' + str(m['lineNumber']) + ': ' + str(arg)
|
||||
|
||||
if m['level'] == 'log':
|
||||
color = INFO
|
||||
channel = 'I/'
|
||||
elif m['level'] == 'warn':
|
||||
color = WARNING
|
||||
channel = 'W/'
|
||||
elif m['level'] == 'error':
|
||||
color = FAIL
|
||||
channel = 'E/'
|
||||
else:
|
||||
color = INFO
|
||||
channel = m['level'] + '/'
|
||||
|
||||
text = set_color(channel + text, color)
|
||||
print text
|
||||
reset_color()
|
||||
|
||||
msgs = send_b2g_cmd(consoleActor, 'getCachedMessages', { 'messageTypes': ['PageError', 'ConsoleAPI'] })
|
||||
log_b2g_message(msgs)
|
||||
|
||||
while True:
|
||||
msg = read_b2g_response()
|
||||
log_b2g_message(msg)
|
||||
else:
|
||||
print 'Application "' + sys.argv[2] + '" is not running!'
|
||||
|
||||
def b2g_screenshot(filename):
|
||||
global deviceActorName
|
||||
data_reply = send_b2g_cmd(deviceActorName, 'screenshotToDataURL')
|
||||
data = data_reply['value']
|
||||
if not isinstance(data, basestring): # The device is sending the screenshot in multiple fragments since it's too long to fit in one message?
|
||||
data_get_actor = data['actor']
|
||||
data_len = int(data['length'])
|
||||
data = data['initial']
|
||||
chunk_size = 65000
|
||||
pos = len(data)
|
||||
# Pull and assemble individual screenshot fragments.
|
||||
while pos < data_len:
|
||||
bytes_to_read = min(data_len - pos, chunk_size)
|
||||
data_reply = send_b2g_cmd(data_get_actor, 'substring', { 'start': str(pos), 'end': str(pos + bytes_to_read) })
|
||||
if len(data_reply['substring']) != bytes_to_read:
|
||||
print >> sys.stderr, 'Error! Expected to receive ' + str(bytes_to_read) + ' bytes of image data, but got ' + str(len(data_reply['substring'])) + ' bytes instead!'
|
||||
sys.exit(1)
|
||||
data += data_reply['substring']
|
||||
pos += bytes_to_read
|
||||
send_b2g_cmd(data_get_actor, 'release') # We need to explicitly free the screenshot image string from the device, or the Devtools connection leaks resources!
|
||||
|
||||
# Expected format is "data:image/png;base64,<base64data>"
|
||||
delim = re.search(",", data).start()
|
||||
data_format = data[:delim]
|
||||
if data_format != "data:image/png;base64":
|
||||
print >> sys.stderr, "Error: Received screenshot from device in an unexpected format '" + data_format + "'!"
|
||||
sys.exit(1)
|
||||
data = data[delim+1:]
|
||||
|
||||
binary_data = base64.b64decode(data)
|
||||
open(filename, 'wb').write(binary_data)
|
||||
|
||||
def get_png_image_size(filename):
|
||||
fhandle = open(filename, 'rb')
|
||||
head = fhandle.read(24)
|
||||
if len(head) != 24:
|
||||
return (-1, -1)
|
||||
check = struct.unpack('>i', head[4:8])[0]
|
||||
if check != 0x0d0a1a0a:
|
||||
return (-1, -1)
|
||||
return struct.unpack('>ii', head[16:24])
|
||||
|
||||
width, height = get_png_image_size(filename)
|
||||
if width <= 0 or height <= 0:
|
||||
print >> sys.stderr, "Wrote " + sizeof_fmt(len(binary_data)) + " to file '" + filename + "', but the contents may be corrupted!"
|
||||
else:
|
||||
print "Wrote " + sizeof_fmt(len(binary_data)) + " to file '" + filename + "' (" + str(width) + 'x' + str(height) + ' pixels).'
|
||||
|
||||
def b2g_get_description(desc):
|
||||
global deviceActorName
|
||||
data_reply = send_b2g_cmd(deviceActorName, 'getDescription')
|
||||
# First try an exact match to requested desc
|
||||
if desc and desc in data_reply['value']:
|
||||
print desc + ': ' + str(data_reply['value'][desc])
|
||||
else: # Print all with case-insensitive substring search
|
||||
for k,v in data_reply['value'].items():
|
||||
if not desc or desc.lower() in k.lower():
|
||||
print k + ': ' + str(v)
|
||||
|
||||
def main():
|
||||
global b2g_socket, webappsActorName
|
||||
global b2g_socket, webappsActorName, deviceActorName, HOST, PORT, VERBOSE, ADB
|
||||
if len(sys.argv) < 2 or '--help' in sys.argv or 'help' in sys.argv or '-v' in sys.argv:
|
||||
print '''Firefox OS Debug Bridge, a tool for automating FFOS device tasks from the command line.
|
||||
|
||||
|
@ -140,30 +478,96 @@ def main():
|
|||
list [--running] [--all]: Prints out the user applications installed on the device.
|
||||
If --running is passed, only the currently opened apps are shown.
|
||||
If --all is specified, then also uninstallable system applications are listed.
|
||||
launch <app>: Starts the given application. If already running, brings to front.
|
||||
launch <app> [--log]: Starts the given application. If already running, brings to front. If the --log option is passed, ffdb will
|
||||
start persistently logging the execution of the given application.
|
||||
close <app>: Terminates the execution of the given application.
|
||||
uninstall <app>: Removes the given application from the device.
|
||||
install <path>: Uploads and installs a packaged app that resides in the given local directory.
|
||||
install <path> [--run] [--log]: Uploads and installs a packaged app that resides in the given local directory.
|
||||
<path> may either refer to a directory containing a packaged app, or to a prepackaged zip file.
|
||||
If the --run option is passed, the given application is immediately launched after the installation finishes.
|
||||
If the --log option is passed, ffdb will start persistently logging the execution of the installed application.
|
||||
log <app> [--clear]: Starts a persistent log listener that reads web console messages from the given application.
|
||||
If --clear is passed, the message log for that application is cleared instead.
|
||||
memory <app>: Dumps a memory usage summary for the given application.
|
||||
navigate <url>: Opens the given web page in the B2G browser.
|
||||
screenshot [filename.png]: Takes a screenshot of the current contents displayed on the device. If an optional
|
||||
filename is specified, the screenshot is saved to that file. Otherwise the filename
|
||||
will be autogenerated.
|
||||
get [pref]: Fetches the value of the given developer pref option from the FFOS device and prints it to console. The parameter pref
|
||||
is optional and may either be the full name of a pref, or a substring to search for. All matching prefs will be printed.
|
||||
If no pref parameter is given, all prefs are printed.
|
||||
NOTE: This function (currently at least) only reports prefs that have been explicitly set and don't have their default value.
|
||||
set <pref> <value>: Writes the given pref option to the FFOS device and restarts the B2G process on it for the change to take effect.
|
||||
unset <pref>: Removes the given pref option from the FFOS device and restarts the B2G process on it for the change to take effect.
|
||||
|
||||
hide-prompt: Permanently removes the remote debugging connection dialog from showing up, and reboots the phone. This command is
|
||||
provided for conveniency, and is the same as calling './ffdb.py set devtools.debugger.prompt-connection false'
|
||||
restore-prompt: Restores the remote debugging connection dialog prompt to its default state.
|
||||
|
||||
desc [desc]: Fetches the value of the given device description field. These fields are read-only and describe the current system.
|
||||
If the optional desc parameter is omitted, all device descriptions are printed. Otherwise the given description is
|
||||
printed if it is an exact match, or all descriptions containing desc as the substring are printed.
|
||||
|
||||
Options: Additionally, the following options may be passed to control FFDB execution:
|
||||
|
||||
--host <hostname>: Specifies the target network address to connect to. Default: 'localhost'.
|
||||
--port <number>: Specifies the network port to connect to. Default: 6000.
|
||||
--verbose: Enables verbose printing, mostly useful for debugging.
|
||||
--simulator: Signal that we will be connecting to a FFOS simulator and not a real device.
|
||||
|
||||
In the above, whenever a command requires an <app> to be specified, either the human-readable name,
|
||||
localId or manifestURL of the application can be used.'''
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
connect_to_simulator = False
|
||||
|
||||
options_with_value = ['--host', '--port']
|
||||
options = options_with_value + ['--verbose', '--simulator']
|
||||
# Process options
|
||||
for i in range(0, len(sys.argv)):
|
||||
if sys.argv[i] in options_with_value:
|
||||
if i+1 >= sys.argv or sys.argv[i+1].startswith('-'):
|
||||
print >> sys.stderr, "Missing value for option " + sys.argv[i] +'!'
|
||||
sys.exit(1)
|
||||
if sys.argv[i] == '--host':
|
||||
HOST = sys.argv[i+1]
|
||||
elif sys.argv[i] == '--port':
|
||||
PORT = int(sys.argv[i+1])
|
||||
elif sys.argv[i] == '--verbose':
|
||||
VERBOSE = True
|
||||
elif sys.argv[i] == '--simulator':
|
||||
connect_to_simulator = True
|
||||
|
||||
# Clear the processed options so that parsing the commands below won't trip up on these.
|
||||
if sys.argv[i] in options: sys.argv[i] = ''
|
||||
if sys.argv[i] in options_with_value: sys.argv[i+1] = ''
|
||||
|
||||
sys.argv = filter(lambda x: len(x) > 0, sys.argv)
|
||||
|
||||
# Double-check that the device is found via adb:
|
||||
if (HOST == 'localhost' or HOST == '127.0.0.1') and not connect_to_simulator:
|
||||
devices = adb_devices()
|
||||
if len(devices) == 0:
|
||||
print 'Error! Failed to connect to B2G device debugger socket at address ' + HOST + ':' + str(PORT) + ' and no devices were detected via adb. Please double-check the following and try again: '
|
||||
print ' 1) The device is powered on and connected to the computer with an USB cable.'
|
||||
print ' 2) ADB and DevTools debugging is enabled on the device. (Settings -> Developer -> Debugging via USB: "ADB and DevTools"'
|
||||
print ' 3) The device is listed when you run "adb devices" on the command line.'
|
||||
print ' 4) When launching ffdb, remember to acknowledge the "incoming debug connection" dialog if it pops up on the device.'
|
||||
sys.exit(1)
|
||||
b2g_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
if WINDOWS:
|
||||
# Python Windows issue: user cannot press Ctrl-C to abort from a socket .recv() Therefore simulate blocking sockets with looping over reads that time out.
|
||||
b2g_socket.settimeout(0.5)
|
||||
try:
|
||||
b2g_socket.connect((HOST, PORT))
|
||||
except Exception, e:
|
||||
if e[0] == 61: # Connection refused
|
||||
if HOST == 'localhost' or HOST == '127.0.0.1':
|
||||
cmd = ['adb', 'forward', 'tcp:'+str(PORT), 'localfilesystem:/data/local/debugger-socket']
|
||||
if (HOST == 'localhost' or HOST == '127.0.0.1') and not connect_to_simulator:
|
||||
cmd = [ADB, 'forward', 'tcp:'+str(PORT), 'localfilesystem:/data/local/debugger-socket']
|
||||
print 'Connection to ' + HOST + ':' + str(PORT) + ' refused, attempting to forward device debugger-socket to local address by calling ' + str(cmd) + ':'
|
||||
else:
|
||||
print 'Error! Failed to connect to B2G device debugger socket at address ' + HOST + ':' + str(PORT) + '!'
|
||||
print 'Error! Failed to connect to B2G ' + ('simulator' if connect_to_simulator else 'device') + ' debugger socket at address ' + HOST + ':' + str(PORT) + '!'
|
||||
sys.exit(1)
|
||||
try:
|
||||
retcode = subprocess.check_call(cmd)
|
||||
|
@ -187,6 +591,9 @@ def main():
|
|||
logv('Connected. Handshake: ' + str(handshake))
|
||||
|
||||
data = send_b2g_cmd('root', 'listTabs')
|
||||
if not 'deviceActor' in data:
|
||||
print 'Error! Debugging connection was not available. Make sure that the "Remote debugging" developer option on the device is set to "ADB and Devtools".'
|
||||
sys.exit(1)
|
||||
deviceActorName = data['deviceActor']
|
||||
logv('deviceActor: ' + deviceActorName)
|
||||
webappsActorName = data['webappsActor']
|
||||
|
@ -195,9 +602,8 @@ def main():
|
|||
send_b2g_cmd(deviceActorName, 'getDescription')
|
||||
send_b2g_cmd(deviceActorName, 'getRawPermissionsTable')
|
||||
|
||||
apps = b2g_get_appslist()
|
||||
|
||||
if sys.argv[1] == 'list':
|
||||
apps = b2g_get_appslist()
|
||||
running_app_manifests = b2g_get_runningapps()
|
||||
printed_apps = apps
|
||||
print_only_running = '--running' in sys.argv and not '--all' in sys.argv
|
||||
|
@ -218,54 +624,27 @@ def main():
|
|||
if len(sys.argv) < 3:
|
||||
print 'Error! No application name given! Usage: ' + sys.argv[0] + ' ' + sys.argv[1] + ' <app>'
|
||||
return 1
|
||||
for app in apps:
|
||||
if str(app['localId']) == sys.argv[2] or app['name'] == sys.argv[2] or app['manifestURL'] == sys.argv[2]:
|
||||
send_b2g_cmd(webappsActorName, sys.argv[1], { 'manifestURL': app['manifestURL'] })
|
||||
return 0
|
||||
print 'Error! Application "' + sys.argv[2] + '" was not found! Use the \'list\' command to find installed applications.'
|
||||
return 1
|
||||
ret = b2g_app_command(sys.argv[1], sys.argv[2])
|
||||
if ret == 0 and '--log' in sys.argv:
|
||||
b2g_log(sys.argv[2])
|
||||
elif sys.argv[1] == 'install':
|
||||
if len(sys.argv) < 3:
|
||||
print 'Error! No application path given! Usage: ' + sys.argv[0] + ' ' + sys.argv[1] + ' <path>'
|
||||
return 1
|
||||
target_app_path = sys.argv[2]
|
||||
if os.path.isdir(target_app_path):
|
||||
print 'Zipping up the contents of directory "' + target_app_path + '"...'
|
||||
(oshandle, tempzip) = tempfile.mkstemp(suffix='.zip', prefix='ffdb_temp_')
|
||||
zipdir(target_app_path, tempzip)
|
||||
target_app_path = tempzip
|
||||
# Remember to delete the temporary package after we quit.
|
||||
def delete_temp_file():
|
||||
os.remove(tempzip)
|
||||
atexit.register(delete_temp_file)
|
||||
|
||||
print 'Uploading application package "' + target_app_path + '"...'
|
||||
print 'Size of compressed package: ' + sizeof_fmt(os.path.getsize(target_app_path)) + '.'
|
||||
uploadResponse = send_b2g_cmd(webappsActorName, 'uploadPackage')
|
||||
packageUploadActor = uploadResponse['actor']
|
||||
app_file = open(target_app_path, 'rb')
|
||||
data = app_file.read()
|
||||
file_size = len(data)
|
||||
chunk_size = 4*1024*1024
|
||||
i = 0
|
||||
start_time = time.time()
|
||||
while i < file_size:
|
||||
chunk = data[i:i+chunk_size]
|
||||
|
||||
send_b2g_data_chunk(packageUploadActor, chunk)
|
||||
i += chunk_size
|
||||
bytes_uploaded = min(i, file_size)
|
||||
cur_time = time.time()
|
||||
secs_elapsed = cur_time - start_time
|
||||
percentage_done = bytes_uploaded * 1.0 / file_size
|
||||
total_time = secs_elapsed / percentage_done
|
||||
time_left = total_time - secs_elapsed
|
||||
print sizeof_fmt(bytes_uploaded) + " uploaded, {:5.1f} % done.".format(percentage_done*100.0) + ' Elapsed: ' + str(int(secs_elapsed)) + ' seconds. Time left: ' + str(datetime.timedelta(seconds=int(time_left))) + '. Data rate: {:5.2f} KB/second.'.format(bytes_uploaded / 1024.0 / secs_elapsed)
|
||||
send_b2g_cmd(webappsActorName, 'install', { 'appId': str(uuid.uuid4()), 'upload': packageUploadActor })
|
||||
|
||||
cur_time = time.time()
|
||||
secs_elapsed = cur_time - start_time
|
||||
print 'Upload of ' + sizeof_fmt(file_size) + ' finished. Total time elapsed: ' + str(int(secs_elapsed)) + ' seconds. Data rate: {:5.2f} KB/second.'.format(file_size / 1024.0 / secs_elapsed)
|
||||
# Kill and uninstall old running app execution before starting.
|
||||
if '--run' in sys.argv:
|
||||
app_manifest = get_packaged_app_manifest(target_app_path)
|
||||
b2g_app_command('close', app_manifest['name'], print_errors_to_console=False)
|
||||
b2g_app_command('uninstall', app_manifest['name'], print_errors_to_console=False)
|
||||
# Upload package
|
||||
app_id = b2g_install(target_app_path)
|
||||
# Launch it immediately if requested.
|
||||
if '--run' in sys.argv:
|
||||
b2g_app_command('launch', app_id)
|
||||
# Don't quit, but keep logging the app if requested.
|
||||
if '--log' in sys.argv:
|
||||
b2g_log(app_id)
|
||||
elif sys.argv[1] == 'navigate':
|
||||
if len(sys.argv) < 3:
|
||||
print 'Error! No URL given! Usage: ' + sys.argv[0] + ' ' + sys.argv[1] + ' <url>'
|
||||
|
@ -281,55 +660,44 @@ def main():
|
|||
else:
|
||||
print 'Web browser is not running!'
|
||||
elif sys.argv[1] == 'log':
|
||||
appActor = ''
|
||||
for app in apps:
|
||||
if str(app['localId']) == sys.argv[2] or app['name'] == sys.argv[2] or app['manifestURL'] == sys.argv[2]:
|
||||
appActor = send_b2g_cmd(webappsActorName, 'getAppActor', { 'manifestURL': app['manifestURL'] })
|
||||
break
|
||||
if 'actor' in appActor:
|
||||
consoleActor = appActor['actor']['consoleActor']
|
||||
|
||||
if '-c' in sys.argv or '-clear' in sys.argv or '--clear' in sys.argv:
|
||||
send_b2g_cmd(consoleActor, 'clearMessagesCache')
|
||||
print 'Cleared message log.'
|
||||
sys.exit(0)
|
||||
|
||||
msgs = send_b2g_cmd(consoleActor, 'startListeners', { 'listeners': ['PageError','ConsoleAPI','NetworkActivity','FileActivity'] })
|
||||
|
||||
def log_b2g_message(msg):
|
||||
WARNING = '\033[93m'
|
||||
FAIL = '\033[91m'
|
||||
ENDC = '\033[0m'
|
||||
BOLD = "\033[1m"
|
||||
msgs = []
|
||||
if 'type' in msg and msg['type'] == 'consoleAPICall':
|
||||
msgs = [msg['message']]
|
||||
elif 'messages' in msg:
|
||||
msgs = msg['messages']
|
||||
|
||||
for m in msgs:
|
||||
args = m['arguments']
|
||||
|
||||
for arg in args:
|
||||
if m['level'] == 'log':
|
||||
color = 'I/'
|
||||
elif m['level'] == 'warn':
|
||||
color = WARNING + 'W/'
|
||||
elif m['level'] == 'error':
|
||||
color = FAIL + 'E/'
|
||||
else:
|
||||
color = m['level'] + '/'
|
||||
|
||||
print color + str(m['functionName']) + '@' + str(m['filename']) + ':' + str(m['lineNumber']) + ': ' + str(arg) + ENDC
|
||||
|
||||
msgs = send_b2g_cmd(consoleActor, 'getCachedMessages', { 'messageTypes': ['PageError', 'ConsoleAPI'] })
|
||||
log_b2g_message(msgs)
|
||||
|
||||
while True:
|
||||
msg = read_b2g_response()
|
||||
log_b2g_message(msg)
|
||||
clear = '-c' in sys.argv or '-clear' in sys.argv or '--clear' in sys.argv
|
||||
b2g_log(sys.argv[2], clear)
|
||||
elif sys.argv[1] == 'memory':
|
||||
b2g_memory(sys.argv[2])
|
||||
elif sys.argv[1] == 'screenshot':
|
||||
if len(sys.argv) >= 3:
|
||||
filename = sys.argv[2]
|
||||
if not filename.endswith('.png'):
|
||||
print >> sys.stderr, "Writing screenshots only to .png files are supported!"
|
||||
sys.exit(1)
|
||||
else:
|
||||
print 'Application "' + sys.argv[2] + '" is not running!'
|
||||
filename = time.strftime("screen_%Y%m%d_%H%M%S.png", time.gmtime())
|
||||
|
||||
b2g_screenshot(filename)
|
||||
elif sys.argv[1] == 'get':
|
||||
b2g_get_pref(sys.argv[2] if len(sys.argv) >= 3 else None)
|
||||
elif sys.argv[1] == 'set':
|
||||
if len(sys.argv) < 3:
|
||||
print 'Error! No pref name to set given! Usage: ' + sys.argv[0] + ' ' + sys.argv[1] + ' <pref> <value>'
|
||||
sys.exit(1)
|
||||
if len(sys.argv) < 4:
|
||||
print 'Error! No value given to set! Usage: ' + sys.argv[0] + ' ' + sys.argv[1] + ' <pref> <value>'
|
||||
sys.exit(1)
|
||||
if len(sys.argv) > 4:
|
||||
print 'Error! Too many arguments given (' + str(sys.argv) + '), need exactly four! Usage: ' + sys.argv[0] + ' ' + sys.argv[1] + ' <pref> <value>'
|
||||
sys.exit(1)
|
||||
b2g_set_pref(sys.argv[2], sys.argv[3])
|
||||
elif sys.argv[1] == 'unset':
|
||||
if len(sys.argv) < 3:
|
||||
print 'Error! No pref name given! Usage: ' + sys.argv[0] + ' ' + sys.argv[1] + ' <pref>'
|
||||
sys.exit(1)
|
||||
b2g_set_pref(sys.argv[2], None)
|
||||
elif sys.argv[1] == 'hide-prompt':
|
||||
b2g_set_pref('devtools.debugger.prompt-connection', 'false')
|
||||
elif sys.argv[1] == 'restore-prompt':
|
||||
b2g_set_pref('devtools.debugger.prompt-connection', None)
|
||||
elif sys.argv[1] == 'desc':
|
||||
b2g_get_description(sys.argv[2] if len(sys.argv) >= 3 else None)
|
||||
else:
|
||||
print "Unknown command '" + sys.argv[1] + "'! Pass --help for instructions."
|
||||
|
||||
|
@ -337,6 +705,10 @@ def main():
|
|||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
returncode = main()
|
||||
logv('ffdb.py quitting with process exit code ' + str(returncode))
|
||||
sys.exit(returncode)
|
||||
try:
|
||||
returncode = main()
|
||||
logv('ffdb.py quitting with process exit code ' + str(returncode))
|
||||
sys.exit(returncode)
|
||||
except KeyboardInterrupt:
|
||||
print ('^C' if WINDOWS else '') + ' Aborted by user'
|
||||
sys.exit(1)
|
||||
|
|
|
@ -128,10 +128,8 @@ load('utility.js');
|
|||
|
||||
// Utilities
|
||||
|
||||
var FUNCTION = set('defun', 'function');
|
||||
var LOOP = set('do', 'while', 'for');
|
||||
var LOOP_FLOW = set('break', 'continue');
|
||||
var ASSIGN_OR_ALTER = set('assign', 'unary-postfix', 'unary-prefix');
|
||||
var CONTROL_FLOW = set('do', 'while', 'for', 'if', 'switch');
|
||||
var NAME_OR_NUM = set('name', 'num');
|
||||
var ASSOCIATIVE_BINARIES = set('+', '*', '|', '&', '^');
|
||||
|
@ -145,10 +143,7 @@ var COMMABLE = set('assign', 'binary', 'unary-prefix', 'unary-postfix', 'name',
|
|||
|
||||
var FUNCTIONS_THAT_ALWAYS_THROW = set('abort', '___resumeException', '___cxa_throw', '___cxa_rethrow');
|
||||
|
||||
var NULL_NODE = ['name', 'null'];
|
||||
var UNDEFINED_NODE = ['unary-prefix', 'void', ['num', 0]];
|
||||
var TRUE_NODE = ['unary-prefix', '!', ['num', 0]];
|
||||
var FALSE_NODE = ['unary-prefix', '!', ['num', 1]];
|
||||
|
||||
var GENERATED_FUNCTIONS_MARKER = '// EMSCRIPTEN_GENERATED_FUNCTIONS';
|
||||
var generatedFunctions = false; // whether we have received only generated functions
|
||||
|
|
|
@ -706,10 +706,8 @@ if USE_EMSDK:
|
|||
path_from_root('system', 'include', 'compat'),
|
||||
path_from_root('system', 'include'),
|
||||
path_from_root('system', 'include', 'emscripten'),
|
||||
path_from_root('system', 'include', 'bsd'), # posix stuff
|
||||
path_from_root('system', 'include', 'libc'),
|
||||
path_from_root('system', 'include', 'gfx'),
|
||||
path_from_root('system', 'include', 'net'),
|
||||
path_from_root('system', 'include', 'SDL'),
|
||||
]
|
||||
|
||||
|
@ -1426,12 +1424,17 @@ class Building:
|
|||
settings = Settings.serialize()
|
||||
args = settings + extra_args
|
||||
if WINDOWS:
|
||||
args = ['@' + response_file.create_response_file(args, TEMP_DIR)]
|
||||
rsp_file = response_file.create_response_file(args, TEMP_DIR)
|
||||
args = ['@' + rsp_file]
|
||||
cmdline = [PYTHON, EMSCRIPTEN, filename + ('.o.ll' if append_ext else ''), '-o', filename + '.o.js'] + args
|
||||
if jsrun.TRACK_PROCESS_SPAWNS:
|
||||
logging.info('Executing emscripten.py compiler with cmdline "' + ' '.join(cmdline) + '"')
|
||||
jsrun.timeout_run(Popen(cmdline, stdout=PIPE), None, 'Compiling')
|
||||
|
||||
# Clean up .rsp file the compiler used after we are finished.
|
||||
if WINDOWS:
|
||||
try_delete(rsp_file)
|
||||
|
||||
# Detect compilation crashes and errors
|
||||
assert os.path.exists(filename + '.o.js'), 'Emscripten failed to generate .js'
|
||||
|
||||
|
|
|
@ -427,12 +427,18 @@ def calculate(temp_files, in_temp, stdout, stderr):
|
|||
# TODO: Move all __deps from src/library*.js to deps_info.json, and use that single source of info
|
||||
# both here and in the JS compiler.
|
||||
deps_info = json.loads(open(shared.path_from_root('src', 'deps_info.json')).read())
|
||||
added = set()
|
||||
def add_back_deps(need):
|
||||
more = False
|
||||
for ident, deps in deps_info.iteritems():
|
||||
if ident in need.undefs:
|
||||
if ident in need.undefs and not ident in added:
|
||||
added.add(ident)
|
||||
more = True
|
||||
for dep in deps:
|
||||
need.undefs.add(dep)
|
||||
shared.Settings.EXPORTED_FUNCTIONS.append('_' + dep)
|
||||
if more:
|
||||
add_back_deps(need) # recurse to get deps of deps
|
||||
for symbols in symbolses:
|
||||
add_back_deps(symbols)
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче