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);