diff --git a/index.html b/index.html index 1ce84a6d79..2aa4baf3d6 100644 --- a/index.html +++ b/index.html @@ -512,7 +512,7 @@ var previousInput = ''; var sourceAnnotations = []; - var compile = function() { + var compile = function(missingInputs) { editor.getSession().clearAnnotations(); sourceAnnotations = []; editor.getSession().removeMarker(errMarkerId); @@ -522,13 +522,17 @@ var files = {}; files[fileNameFromKey(SOL_CACHE_FILE)] = input; - var input = gatherImports(files, compile); - if (!input) return; - var optimize = document.querySelector('#optimize').checked; - - compileJSON(input, optimize ? 1 : 0); + gatherImports(files, missingInputs, function(input, error) { + $('#output').empty(); + if (input === null) { + renderError(error); + } else { + var optimize = document.querySelector('#optimize').checked; + compileJSON(input, optimize ? 1 : 0); + } + }); }; - var compilationFinished = function(result) { + var compilationFinished = function(result, missingInputs) { var data = $.parseJSON(result); var noFatalErrors = true; // ie warnings are ok @@ -543,7 +547,10 @@ }); } - if (noFatalErrors && !hidingRHP) renderContracts(data, editor.getValue()); + if (missingInputs !== undefined && missingInputs.length > 0) + compile(missingInputs); + else if (noFatalErrors && !hidingRHP) + renderContracts(data, editor.getValue()); }; var compileTimeout = null; @@ -563,7 +570,18 @@ var onCompilerLoaded = function() { if (worker === null) { var compile; - if ('_compileJSONMulti' in Module) { + var missingInputs = []; + if ('_compileJSONCallback' in Module) { + compilerAcceptsMultipleFiles = true; + var missingInputsCallback = Module.Runtime.addFunction(function(path, contents, error) { + missingInputs.push(Module.Pointer_stringify(path)); + }); + var compileInternal = Module.cwrap("compileJSONCallback", "string", ["string", "number", "number"]); + compile = function(input, optimize) { + missingInputs.length = 0; + return compileInternal(input, optimize, missingInputsCallback); + }; + } else if ('_compileJSONMulti' in Module) { compilerAcceptsMultipleFiles = true; compile = Module.cwrap("compileJSONMulti", "string", ["string", "number"]); } else { @@ -576,7 +594,7 @@ } catch (exception) { result = JSON.stringify({error: 'Uncaught JavaScript exception:\n' + exception}); } - compilationFinished(result); + compilationFinished(result, missingInputs); }; $('#version').text(Module.cwrap("version", "string", [])()); } @@ -585,49 +603,54 @@ }; var cachedRemoteFiles = {}; - function gatherImports(files, asyncCallback, needAsync) { + function gatherImports(files, importHints, cb) { + importHints = importHints || []; if (!compilerAcceptsMultipleFiles) - return files[fileNameFromKey(SOL_CACHE_FILE)]; + { + cb(files[fileNameFromKey(SOL_CACHE_FILE)]); + return; + } var importRegex = /import\s[\'\"]([^\'\"]+)[\'\"];/g; var reloop = false; do { reloop = false; for (var fileName in files) { var match; - while (match = importRegex.exec(files[fileName])) { - var m = match[1]; - if (m in files) continue; - if (getFiles().indexOf(fileKey(m)) !== -1) { - files[m] = window.localStorage[fileKey(match[1])]; - reloop = true; - } else if (m in cachedRemoteFiles) { - files[m] = cachedRemoteFiles[m]; - reloop = true; - } else if (githubMatch = /^(https?:\/\/)?(www.)?github.com\/([^\/]*\/[^\/]*)\/(.*)/.exec(m)) { - $.getJSON('https://api.github.com/repos/' + githubMatch[3] + '/contents/' + githubMatch[4], function(result) { - var content; - if ('content' in result) - content = Base64.decode(result.content); - else - content = "\"" + m + "\" NOT FOUND"; //@TODO handle this better - cachedRemoteFiles[m] = content; - files[m] = content; - gatherImports(files, asyncCallback, true); - }).fail(function(){ - var content = "\"" + m + "\" NOT FOUND"; //@TODO handle this better + while (match = importRegex.exec(files[fileName])) + importHints.push(match[1]); + } + while (importHints.length > 0) { + var m = importHints.pop(); + if (m in files) continue; + if (getFiles().indexOf(fileKey(m)) !== -1) { + files[m] = window.localStorage[fileKey(m)]; + reloop = true; + } else if (m in cachedRemoteFiles) { + files[m] = cachedRemoteFiles[m]; + reloop = true; + } else if (githubMatch = /^(https?:\/\/)?(www.)?github.com\/([^\/]*\/[^\/]*)\/(.*)/.exec(m)) { + $('#output').append($('
').append($('').text("Loading github.com/" + githubMatch[3] + " ..."))); + $.getJSON('https://api.github.com/repos/' + githubMatch[3] + '/contents/' + githubMatch[4], function(result) { + if ('content' in result) + { + var content = Base64.decode(result.content); cachedRemoteFiles[m] = content; files[m] = content; - gatherImports(files, asyncCallback, true); - }); - return null; - } + gatherImports(files, importHints, cb); + } + else + cb(null, "Unable to import \"" + m + "\""); + }).fail(function(){ + cb(null, "Unable to import \"" + m + "\""); + }); + return; + } else { + cb(null, "Unable to import \"" + m + "\""); + return; } } } while (reloop); - var input = JSON.stringify({'sources':files}); - if (needAsync) - asyncCallback(input); - return input; + cb(JSON.stringify({'sources':files})); } var initializeWorker = function() { @@ -643,7 +666,7 @@ onCompilerLoaded(); break; case 'compiled': - compilationFinished(data.data); + compilationFinished(data.data, data.missingInputs); break; }; }); diff --git a/index.js b/index.js index ed5f2a7d3b..4e4c14f88d 100644 --- a/index.js +++ b/index.js @@ -5,9 +5,42 @@ function setupMethods (soljson){ '_compileJSONMulti' in soljson ? soljson.cwrap("compileJSONMulti", "string", ["string", "number"]) : null; + var compileJSONCallback = null; + if ('_compileJSONCallback' in soljson) + { + /// TODO: Allocating memory and copying the strings over + /// to the emscripten runtime does not seem to work. + var copyString = function(str, ptr) { + var buffer = soljson._malloc(str.length + 1); + soljson.writeStringToMemory(str, buffer); + soljson.setValue(ptr, buffer, '*'); + }; + var wrapCallback = function(callback) { + return soljson.Runtime.addFunction(function(path, contents, error) { + // path is char*, contents is char**, error is char** + // TODO copying the results does not seem to work. + // This is not too bad, because most of the requests + // cannot be answered synchronously anyway. + var result = callback(soljson.Pointer_stringify(path)); + if (typeof(result.contents) === typeof('')) + copyString(result.contents, contents); + if (typeof(result.error) === typeof('')) + copyString(result.error, error); + }); + }; + var compileInternal = soljson.cwrap("compileJSONCallback", "string", ["string", "number", "number"]); + compileJSONCallback = function(input, optimize, readCallback) { + var cb = wrapCallback(readCallback); + var output = compileInternal(input, optimize, cb); + soljson.Runtime.removeFunction(cb); + return output; + }; + } - var compile = function(input, optimise) { + var compile = function(input, optimise, readCallback) { var result = ''; + if (readCallback !== undefined && compileJSONCallback !== null) + result = compileJSONCallback(JSON.stringify(input), optimise, readCallback); if (typeof(input) != typeof('') && compileJSONMulti !== null) result = compileJSONMulti(JSON.stringify(input), optimise); else diff --git a/worker.js b/worker.js index 2edea48ff7..1a1c714c97 100644 --- a/worker.js +++ b/worker.js @@ -2,6 +2,7 @@ var version = function() { return '(loading)'; } var compileJSON = function() { return ''; } addEventListener('message', function(e) { var data = e.data; + var missingInputs = []; switch (data.cmd) { case 'loadVersion': delete Module; @@ -10,7 +11,17 @@ addEventListener('message', function(e) { importScripts(data.data); version = Module.cwrap("version", "string", []); - if ('_compileJSONMulti' in Module) + if ('_compileJSONCallback' in Module) + { + compileJSONInternal = Module.cwrap("compileJSONCallback", "string", ["string", "number", "number"]); + var missingInputCallback = Module.Runtime.addFunction(function(path) { + missingInputs.push(Module.Pointer_stringify(path)); + }); + compileJSON = function(input, optimize) { + return compileJSONInternal(input, optimize, missingInputCallback); + }; + } + else if ('_compileJSONMulti' in Module) compileJSON = Module.cwrap("compileJSONMulti", "string", ["string", "number"]); else compileJSON = Module.cwrap("compileJSON", "string", ["string", "number"]); @@ -21,7 +32,8 @@ addEventListener('message', function(e) { }); break; case 'compile': - postMessage({cmd: 'compiled', data: compileJSON(data.source, data.optimize)}); + missingInputs + postMessage({cmd: 'compiled', data: compileJSON(data.source, data.optimize)}, missingInputs: missingInputs); break; } }, false);