浏览代码

Merge branch 'grunt'

Conflicts:
	js/Kernel-Tests.js
Nicolas Petton 11 年之前
父节点
当前提交
5601d56f93

+ 2 - 0
.gitignore

@@ -14,4 +14,6 @@ test/run.js
 
 # In case amber is also saved in a local Subversion
 .svn
+
+# Ignoring local NPM modules
 /node_modules/*

+ 174 - 2
bin/amberc

@@ -3,11 +3,183 @@
 var path = require('path');
 var amberc = require('./amberc.js');
 
+// get parameters passed to the command line script
+// discard the first two parameters which are the node binary and the script name
+var parameters = process.argv.slice(2);
+
+// check if at least one parameter was passed to the script
+if (1 > parameters.length) {
+	print_usage();
+	process.exit();
+}
+
+
 // Get Amber root directory from the location of this script so that
 // we can find the st and js directories etc.
 var amber_dir = path.normalize(path.join(path.dirname(process.argv[1]), '..'));
 // Get default location of compiler.jar
 var closure_jar = path.resolve(path.join(process.env['HOME'], 'compiler.jar'));
 
-var compiler = new amberc.Compiler(amber_dir, closure_jar);
-compiler.main();
+var compiler = new amberc.Compiler(amber_dir);
+
+var configuration = handle_options(parameters, amber_dir);
+
+compiler.main(configuration);
+
+
+/**
+ * Process given program options and update defaults values.
+ * Followed by check_for_closure_compiler() and then collect_files().
+ */
+function handle_options(optionsArray, amber_dir) {
+	var programName = [];
+	var currentItem = optionsArray.shift();
+	var defaults = amberc.createDefaults(amber_dir);
+
+	while(undefined !== currentItem) {
+		switch(currentItem) {
+			case '-l':
+				defaults.load.push.apply(defaults.load, optionsArray.shift().split(','));
+				break;
+			case '-i':
+				defaults.init = optionsArray.shift();
+				break;
+			case '-m':
+				defaults.main = optionsArray.shift();
+				break;
+			case '-M':
+				defaults.mainfile = optionsArray.shift();
+				break;
+			case '-o':
+				defaults.closure = true;
+				defaults.closure_parts = true;
+				break;
+			case '-O':
+				defaults.closure = true;
+				defaults.closure_full = true;
+				break;
+			case '-A':
+				defaults.closure = true;
+				defaults.closure_options = defaults.closure_options + ' --compilation_level ADVANCED_OPTIMIZATIONS';
+				defaults.closure_full = true;
+				break;
+			case '-d':
+				defaults.deploy = true;
+				break;
+			case '-s':
+				defaults.suffix = optionsArray.shift();
+				defaults.suffix_used = defaults.suffix;
+				break;
+			case '-S':
+				defaults.loadsuffix = optionsArray.shift();
+				defaults.suffix_used = defaults.suffix;
+				break;
+			case '-h':
+			case '--help':
+			case '?':
+				this.usage();
+				break;
+			default:
+				var fileSuffix = path.extname(currentItem);
+				switch (fileSuffix) {
+					case '.st':
+						defaults.stFiles.push(currentItem);
+						break;
+					case '.js':
+						defaults.jsFiles.push(currentItem);
+						break;
+					default:
+						// Will end up being the last non js/st argument
+						programName.push(currentItem);
+						break;
+				};
+		};
+		currentItem = optionsArray.shift();
+	}
+
+	if(1 < programName.length) {
+		throw new Error('More than one name for ProgramName given: ' + programName);
+	} else {
+		defaults.program = programName[0];
+	}
+	return defaults;
+};
+
+
+// print available flags
+function print_usage() {
+	console.log('Usage: amberc [-l lib1,lib2...] [-i init_file] [-m main_class] [-M main_file]');
+	console.log('          [-o] [-O|-A] [-d] [-s suffix] [-S suffix] [file1 [file2 ...]] [Program]');
+	console.log('');
+	console.log('   amberc compiles Amber files - either separately or into a complete runnable');
+	console.log('   program. If no .st files are listed only a linking stage is performed.');
+	console.log('   Files listed will be handled using the following rules:');
+	console.log('');
+	console.log('   *.js');
+	console.log('     Files are linked (concatenated) in listed order.');
+	console.log('     If not found we look in $AMBER/js/');
+	console.log('');
+	console.log('   *.st');
+	console.log('     Files are compiled into .js files before concatenation.');
+	console.log('     If not found we look in $AMBER/st/.');
+	console.log('');
+	console.log('     NOTE: Each .st file is currently considered to be a fileout of a single class');
+	console.log('     category of the same name as the file!');
+	console.log('');
+	console.log('   If no <Program> is specified each given .st file will be compiled into');
+	console.log('   a matching .js file. Otherwise a <Program>.js file is linked together based on');
+	console.log('   the given options:');
+	console.log('  -l library1,library2');
+	console.log('     Add listed JavaScript libraries in listed order.');
+	console.log('     Libraries are not separated by spaces or end with .js.');
+	console.log('');
+	console.log('  -i init_file');
+	console.log('     Add library initializer <init_file> instead of default $AMBER/js/init.js ');
+	console.log('');
+	console.log('  -m main_class');
+	console.log('     Add a call to the class method main_class>>main at the end of <Program>.');
+	console.log('');
+	console.log('  -M main_file');
+	console.log('     Add <main_file> at the end of <Program.js> acting as #main.');
+	console.log('');
+	console.log('  -o');
+	console.log('     Optimize each .js file using the Google closure compiler.');
+	console.log('     Using Closure compiler found at ~/compiler.jar');
+	console.log('');
+	console.log('  -O');
+	console.log('     Optimize final <Program>.js using the Google closure compiler.');
+	console.log('     Using Closure compiler found at ~/compiler.jar');
+	console.log('');
+	console.log('  -A Same as -O but use --compilation_level ADVANCED_OPTIMIZATIONS');
+	console.log('');
+	console.log('  -d');
+	console.log('     Additionally export code for deploy - stripped from source etc.');
+	console.log('     Uses suffix ".deploy.js" in addition to any explicit suffic set by -s.');
+	console.log('');
+	console.log('  -s suffix');
+	console.log('     Add <suffix> to compiled .js files. File.st is then compiled into');
+	console.log('     File.<suffix>.js.');
+	console.log('');
+	console.log('  -S suffix');
+	console.log('     Use <suffix> for all libraries accessed using -l. This makes it possible');
+	console.log('     to have multiple flavors of Amber and libraries in the same place.');
+	console.log('');
+	console.log('');
+	console.log('     Example invocations:');
+	console.log('');
+	console.log('     Just compile Kernel-Objects.st to Kernel-Objects.js:');
+	console.log('');
+	console.log('        amberc Kernel-Objects.st');
+	console.log('');
+	console.log('     Compile Hello.st to Hello.js and create complete program called Program.js.');
+	console.log('     Additionally add a call to the class method Hello>>main:');
+	console.log('');
+	console.log('        amberc -m Hello Hello.st Program');
+	console.log('');
+	console.log('     Compile Cat1.st and Cat2.st files into corresponding .js files.');
+	console.log('     Link them with myboot.js and myKernel.js and add myinit.js as custom');
+	console.log('     initializer file. Add main.js last which contains the startup code');
+	console.log('      and merge everything into a complete program named Program.js:');
+	console.log('');
+	console.log('        amberc -M main.js -i myinit.js myboot.js myKernel.js Cat1.st Cat2.st Program');
+};

+ 34 - 171
bin/amberc.js

@@ -86,8 +86,8 @@ function AmberC(amber_dir, closure_jar) {
 	this.kernel_libraries = ['boot', 'Kernel-Objects', 'Kernel-Classes', 'Kernel-Methods',
 	                         'Kernel-Collections', 'Kernel-Exceptions', 'Kernel-Transcript',
 	                         'Kernel-Announcements'];
-	this.compiler_libraries = this.kernel_libraries.concat(['parser', 'Compiler', 'Compiler-Exceptions']);
-	                          //, 'Compiler-Core', 'Compiler-AST', 'Compiler-IR', 'Compiler-Inlining', 'Compiler-Semantic'];
+	this.compiler_libraries = this.kernel_libraries.concat(['parser', 'Importer-Exporter', 'Compiler-Exceptions',
+	                          'Compiler-Core', 'Compiler-AST', 'Compiler-IR', 'Compiler-Inlining', 'Compiler-Semantic']);
 }
 
 
@@ -95,12 +95,17 @@ function AmberC(amber_dir, closure_jar) {
  * Default values.
  */
 var createDefaults = function(amber_dir, finished_callback){
+	if (undefined === amber_dir) {
+		throw new Error('createDefaults() function needs a valid amber_dir parameter');
+	}
+
 	return {
-		'smalltalk': {}, // the evaluated compiler will be stored in this variable (see create_compiler)
 		'load': [],
 		'init': path.join(amber_dir, 'js', 'init.js'),
 		'main': undefined,
 		'mainfile': undefined,
+		'stFiles': [],
+		'jsFiles': [],
 		'closure': false,
 		'closure_parts': false,
 		'closure_full': false,
@@ -122,184 +127,38 @@ var createDefaults = function(amber_dir, finished_callback){
 /**
  * Main function for executing the compiler.
  */
-AmberC.prototype.main = function(parameters, finished_callback) {
+AmberC.prototype.main = function(configuration, finished_callback) {
 	console.time('Compile Time');
-	var options = parameters || process.argv.slice(2);
+	if (undefined !== finished_callback) {
+		configuration.finished_callback = finished_callback;
+	}
 
-	if (1 > options.length) {
-		this.usage();
-	} else {
-		this.defaults = createDefaults(this.amber_dir, finished_callback);
-		this.handle_options(options);
+	if (this.check_configuration_ok(configuration)) {
+		this.defaults = configuration;
+		this.defaults.smalltalk = {}; // the evaluated compiler will be stored in this variable (see create_compiler)
+		var self = this;
+		this.check_for_closure_compiler(function(){
+			self.collect_files(self.defaults.stFiles, self.defaults.jsFiles)
+		});
 	}
 };
 
 
 /**
- * Process given program options and update defaults values.
- * Followed by check_for_closure_compiler() and then collect_files().
+ * Check if the passed in configuration object has sufficient/nonconflicting values
  */
-AmberC.prototype.handle_options = function(optionsArray) {
-	var stFiles = [];
-	var jsFiles = [];
-	var programName = [];
-	var currentItem = optionsArray.shift();
-	var defaults = this.defaults;
-
-	while(undefined !== currentItem) {
-		switch(currentItem) {
-			case '-l':
-				defaults.load.push.apply(defaults.load, optionsArray.shift().split(','));
-				break;
-			case '-i':
-				defaults.init = optionsArray.shift();
-				break;
-			case '-m':
-				defaults.main = optionsArray.shift();
-				break;
-			case '-M':
-				defaults.mainfile = optionsArray.shift();
-				break;
-			case '-o':
-				defaults.closure = true;
-				defaults.closure_parts = true;
-				break;
-			case '-O':
-				defaults.closure = true;
-				defaults.closure_full = true;
-				break;
-			case '-A':
-				defaults.closure = true;
-				defaults.closure_options = defaults.closure_options + ' --compilation_level ADVANCED_OPTIMIZATIONS';
-				defaults.closure_full = true;
-				break;
-			case '-d':
-				defaults.deploy = true;
-				break;
-			case '-s':
-				defaults.suffix = optionsArray.shift();
-				defaults.suffix_used = defaults.suffix;
-				break;
-			case '-S':
-				defaults.loadsuffix = optionsArray.shift();
-				defaults.suffix_used = defaults.suffix;
-				break;
-			case '-h':
-			case '--help':
-			case '?':
-				this.usage();
-				break;
-			default:
-				var fileSuffix = path.extname(currentItem);
-				switch (fileSuffix) {
-					case '.st':
-						stFiles.push(currentItem);
-						break;
-					case '.js':
-						jsFiles.push(currentItem);
-						break;
-					default:
-						// Will end up being the last non js/st argument
-						programName.push(currentItem);
-						break;
-				};
-		};
-		currentItem = optionsArray.shift();
+AmberC.prototype.check_configuration_ok = function(configuration) {
+	if (undefined === configuration) {
+		throw new Error('AmberC.check_configuration_ok(): missing configuration object');
 	}
-
-	if(1 < programName.length) {
-		throw new Error('More than one name for ProgramName given: ' + programName);
-	} else {
-		defaults.program = programName[0];
+	if (undefined === configuration.init) {
+		throw new Error('AmberC.check_configuration_ok(): init value missing in configuration object');
 	}
 
-	var self = this;
-	this.check_for_closure_compiler(function(){
-		self.collect_files(stFiles, jsFiles)
-	});
-};
-
-
-/**
- * Print usage options and exit.
- */
-AmberC.prototype.usage = function() {
-	console.log('Usage: amberc [-l lib1,lib2...] [-i init_file] [-m main_class] [-M main_file]');
-	console.log('          [-o] [-O|-A] [-d] [-s suffix] [-S suffix] [file1 [file2 ...]] [Program]');
-	console.log('');
-	console.log('   amberc compiles Amber files - either separately or into a complete runnable');
-	console.log('   program. If no .st files are listed only a linking stage is performed.');
-	console.log('   Files listed will be handled using the following rules:');
-	console.log('');
-	console.log('   *.js');
-	console.log('     Files are linked (concatenated) in listed order.');
-	console.log('     If not found we look in $AMBER/js/');
-	console.log('');
-	console.log('   *.st');
-	console.log('     Files are compiled into .js files before concatenation.');
-	console.log('     If not found we look in $AMBER/st/.');
-	console.log('');
-	console.log('     NOTE: Each .st file is currently considered to be a fileout of a single class');
-	console.log('     category of the same name as the file!');
-	console.log('');
-	console.log('   If no <Program> is specified each given .st file will be compiled into');
-	console.log('   a matching .js file. Otherwise a <Program>.js file is linked together based on');
-	console.log('   the given options:');
-	console.log('  -l library1,library2');
-	console.log('     Add listed JavaScript libraries in listed order.');
-	console.log('     Libraries are not separated by spaces or end with .js.');
-	console.log('');
-	console.log('  -i init_file');
-	console.log('     Add library initializer <init_file> instead of default $AMBER/js/init.js ');
-	console.log('');
-	console.log('  -m main_class');
-	console.log('     Add a call to the class method main_class>>main at the end of <Program>.');
-	console.log('');
-	console.log('  -M main_file');
-	console.log('     Add <main_file> at the end of <Program.js> acting as #main.');
-	console.log('');
-	console.log('  -o');
-	console.log('     Optimize each .js file using the Google closure compiler.');
-	console.log('     Using Closure compiler found at ~/compiler.jar');
-	console.log('');
-	console.log('  -O');
-	console.log('     Optimize final <Program>.js using the Google closure compiler.');
-	console.log('     Using Closure compiler found at ~/compiler.jar');
-	console.log('');
-	console.log('  -A Same as -O but use --compilation_level ADVANCED_OPTIMIZATIONS');
-	console.log('');
-	console.log('  -d');
-	console.log('     Additionally export code for deploy - stripped from source etc.');
-	console.log('     Uses suffix ".deploy.js" in addition to any explicit suffic set by -s.');
-	console.log('');
-	console.log('  -s suffix');
-	console.log('     Add <suffix> to compiled .js files. File.st is then compiled into');
-	console.log('     File.<suffix>.js.');
-	console.log('');
-	console.log('  -S suffix');
-	console.log('     Use <suffix> for all libraries accessed using -l. This makes it possible');
-	console.log('     to have multiple flavors of Amber and libraries in the same place.');
-	console.log('');
-	console.log('');
-	console.log('     Example invocations:');
-	console.log('');
-	console.log('     Just compile Kernel-Objects.st to Kernel-Objects.js:');
-	console.log('');
-	console.log('        amberc Kernel-Objects.st');
-	console.log('');
-	console.log('     Compile Hello.st to Hello.js and create complete program called Program.js.');
-	console.log('     Additionally add a call to the class method Hello>>main:');
-	console.log('');
-	console.log('        amberc -m Hello Hello.st Program');
-	console.log('');
-	console.log('     Compile Cat1.st and Cat2.st files into corresponding .js files.');
-	console.log('     Link them with myboot.js and myKernel.js and add myinit.js as custom');
-	console.log('     initializer file. Add main.js last which contains the startup code');
-	console.log('      and merge everything into a complete program named Program.js:');
-	console.log('');
-	console.log('        amberc -M main.js -i myinit.js myboot.js myKernel.js Cat1.st Cat2.st Program');
-
-	process.exit();
+	if (0 === configuration.jsFiles.length && 0 === configuration.stFiles.lenght) {
+		throw new Error('AmberC.check_configuration_ok(): no files to compile/link specified in configuration object');
+	}
+	return true;
 };
 
 
@@ -547,6 +406,7 @@ AmberC.prototype.create_compiler = function(compilerFilesArray) {
 		content = content + 'return smalltalk;})();';
 		self.defaults.smalltalk = eval(content);
 		console.log('Compiler loaded');
+		self.defaults.smalltalk.ErrorHandler._setCurrent_(self.defaults.smalltalk.RethrowErrorHandler._new());
 
 		self.compile();
 	});
@@ -646,7 +506,9 @@ AmberC.prototype.verify = function() {
  */
 AmberC.prototype.compose_js_files = function() {
 	var defaults = this.defaults;
+	var self = this;
 	if (undefined === defaults.program) {
+		self.optimize();
 		return;
 	}
 	var program_files = [];
@@ -673,7 +535,7 @@ AmberC.prototype.compose_js_files = function() {
 		fileStream.end();
 		console.log(error);
 	});
-	var self = this;
+
 	fileStream.on('close', function(){
 		self.optimize();
 	});
@@ -759,5 +621,6 @@ AmberC.prototype.closure_compile = function(sourceFile, minifiedFile, callback)
 };
 
 module.exports.Compiler = AmberC;
+module.exports.createDefaults = createDefaults;
 module.exports.Combo = Combo;
 module.exports.map = async_map;

+ 191 - 0
grunt.js

@@ -0,0 +1,191 @@
+module.exports = function(grunt) {
+
+  grunt.loadTasks('./grunt/tasks');
+
+  grunt.loadNpmTasks('grunt-image-embed');
+  grunt.loadNpmTasks('grunt-contrib-mincss');
+
+  grunt.registerTask('build:deploy', 'shell:compileDeploy concat:deploy min');
+  grunt.registerTask('build:dev', 'shell:compileDev concat:css imageEmbed mincss css2js concat:dev');
+//  grunt.registerTask('default', 'build:deploy build:dev');
+  grunt.registerTask('default', 'amberc');
+
+  grunt.initConfig({
+    pkg: '<json:package.json>',
+
+    meta: {
+      banner: '/*!\n <%= pkg.title || pkg.name %> - v<%= pkg.version %> - <%= grunt.template.today("yyyy-mm-dd") %> \n License: <%= pkg.license.type %> \n*/\n'
+    },
+
+    pegjs: {
+      amber_parser: {
+        src: 'js/parser.pegjs',
+        dest: 'js/parser.js',
+        trackLineAndColumn: true,
+        cache: false,
+        export_var: 'smalltalk.parser'
+      }
+    },
+
+    amberc: {
+      _config: {
+        amber_dir: process.cwd(),
+        closure_jar: ''
+      },
+      amber_kernel: {
+        working_dir: 'st',
+        target_dir : 'js',
+        src: ['Kernel-Objects.st', 'Kernel-Classes.st', 'Kernel-Methods.st', 'Kernel-Collections.st',
+              'Kernel-Exceptions.st', 'Kernel-Transcript.st', 'Kernel-Announcements.st'],
+        deploy: true
+      },
+      amber_compiler: {
+        working_dir: 'st',
+        target_dir : 'js',
+        src: ['Importer-Exporter.st', 'Compiler.st', 'Compiler-Exceptions.st', 'Compiler-Core.st', 'Compiler-AST.st',
+              'Compiler-IR.st', 'Compiler-Inlining.st', 'Compiler-Semantic.st'],
+        output_name: 'Compiler',
+        deploy: true
+      },
+      amber_canvas: {
+        working_dir: 'st',
+        target_dir : 'js',
+        src: ['Canvas.st', 'SUnit.st'],
+        deploy: true
+      },
+      amber_IDE: {
+        working_dir: 'st',
+        target_dir : 'js',
+        src: ['IDE.st', 'Documentation.st'],
+        libraries: ['Canvas'],
+        deploy: true
+      },
+      amber_tests: {
+        working_dir: 'st',
+        target_dir : 'js',
+        src: ['Kernel-Tests.st', 'Compiler-Tests.st'],
+        libraries: ['SUnit']
+      },
+      amber_examples: {
+        working_dir: 'st',
+        target_dir : 'js',
+        src: ['Examples.st'],
+        libraries: ['Canvas', 'IDE']
+      },
+      amber_dev: {
+        working_dir: 'js',
+        src: ['Kernel-Objects.js', 'Kernel-Classes.js', 'Kernel-Methods.js', 'Kernel-Collections.js',
+              'Kernel-Exceptions.js', 'Kernel-Transcript.js', 'Kernel-Announcements.js',
+              'Compiler.js', 'Compiler-Exceptions.js', 'Compiler-Core.js', 'Compiler-AST.js',
+              'Compiler-IR.js', 'Compiler-Inlining.js', 'Compiler-Semantic.js',
+              'Kernel-Tests.js', 'Compiler-Tests.js',
+              'Canvas.js', 'IDE.js', 'SUnit.js', 'Documentation.js', 'Examples.js'],
+        output_name: 'amber_lib'
+      },
+      server: {
+        working_dir: 'server',
+        src: ['FileServer.st'],
+        main_class: 'FileServer',
+        output_name: 'server'
+      },
+      repl: {
+        working_dir: 'repl',
+        src: ['REPL.st'],
+        main_class: 'Repl',
+        output_name: 'amber'
+      }
+    },
+
+    lint: {
+      amber: ['js/*.js'],
+      server: ['server/*.js'],
+      repl: ['repl/*.js'],
+      tests: ['test/*.js'],
+      grunt: ['grunt.js', 'grunt/**/*.js']
+    },
+
+    concat: {
+      deploy: {
+        src: ['tmp/amber-compiled.deploy.js'],
+        dest: 'dist/amber-<%= pkg.version %>.deploy.js'
+      },
+
+      css: {
+        src: [
+          'css/amber.css',
+          'js/lib/CodeMirror/codemirror.css',
+          'js/lib/CodeMirror/amber.css'
+        ],
+        dest: 'tmp/amber.css'
+      },
+
+      dev: {
+        src: [
+          'js/lib/jQuery/jquery-ui-1.8.16.custom.min.js',
+          'js/lib/jQuery/jquery.textarea.js',
+          'js/lib/CodeMirror/codemirror.js',
+          'js/lib/CodeMirror/smalltalk.js',
+          'tmp/amber-compiled.js',
+          'tmp/css.js'
+        ],
+        dest: 'dist/amber-<%= pkg.version %>.js'
+      }
+    },
+
+    imageEmbed: {
+      dev: {
+        src: ['tmp/amber.css'],
+        dest: 'tmp/amber-images.css',
+        options: {baseDir: 'public'}
+      }
+    },
+
+    mincss: {
+      dev: {
+        src: ['tmp/amber-images.css'],
+        dest: 'tmp/amber.min.css'
+      }
+    },
+
+    css2js: {
+      dev: {
+        src: 'tmp/amber.min.css',
+        dest: 'tmp/css.js'
+      }
+    },
+
+    min: {
+      deploy: {
+        src: 'dist/amber-<%= pkg.version %>.deploy.js',
+        dest: 'dist/amber-<%= pkg.version %>.deploy.min.js'
+      }
+    }
+  });
+
+  grunt.registerMultiTask('css2js', 'Embed CSS into JS', function() {
+    var cssContent = grunt.task.directive(grunt.file.expandFiles(this.data.src)[0], grunt.file.read);
+    var content =
+      'var css="' + cssContent + '";' +
+      'var cssTag = document.createElement("link");' +
+      'document.head = document.head || document.getElementsByTagName("head")[0];' +
+      'cssTag.href = "data:text/css,"+css;' +
+      'cssTag.rel = "stylesheet";' +
+      'document.head.appendChild(cssTag);';
+
+    grunt.file.write(this.data.dest, content);
+
+    grunt.log.writeln('File "' + this.data.dest + '" created.');
+  });
+
+  grunt.registerMultiTask('pegjs', 'Generate JavaScript parser from PEG.js description', function() {
+    var PEG = require('pegjs');
+    var pegOptions = {
+      cache: this.data.cache || false,
+      trackLineAndColumn: this.data.trackLineAndColumn || false
+    };
+    var export_var = this.data.export_var || 'module.exports';
+    var parser = PEG.buildParser(grunt.file.read(this.data.src), pegOptions);
+    var content = export_var + ' = ' + parser.toSource() + ';\n';
+    grunt.file.write(this.data.dest, content);
+  });
+};

+ 145 - 0
grunt/tasks/amberc-grunt.js

@@ -0,0 +1,145 @@
+module.exports = function(grunt) {
+
+  var path = require('path');
+  var fs = require('fs');
+  var amberc = require('../../bin/amberc.js');
+
+  /**
+     Full config looks like this:
+     amberc: {
+       _config: {
+         amber_dir: process.cwd(),     // REQUIRED
+         closure_jar: ''               // optional
+       },
+       helloWorld: {
+         src: ['HelloWorld.st'],                // REQUIRED
+         working_dir: 'projects/HelloWorld/st', // optional
+         target_dir: 'projects/HelloWorld/js',  // optional
+         main_class: 'HelloWorld',              // optional
+         output_name: 'helloWorld',            // optional
+         libraries: 'Canvas',                  // optional
+         init: 'myInit',                       // optional
+         main_file: 'myMain.js',               // optional
+         deploy: true,                         // optional
+         output_suffix: 'mySuffix',            // optional
+         library_suffix: '-0.9',               // optional
+       },
+     },
+
+   */
+  grunt.registerMultiTask('amberc', 'Compile Smalltalk files with the amberc compiler', function() {
+    // mark required properties
+    this.requiresConfig('amberc._config.amber_dir');
+    this.requiresConfig(['amberc', this.target, 'src']);
+
+    // change working directory if the working_dir property is set on the target
+    var current_dir = process.cwd();
+    var working_dir = this.data.working_dir;
+    if (undefined !== working_dir) {
+      grunt.file.setBase(working_dir);
+    }
+
+    // mark task as async task
+    var done = this.async();
+
+    // create and initialize amberc
+    var compiler = new amberc.Compiler(grunt.config('amberc._config.amber_dir'), grunt.config('amberc._config.closure_jar'));
+
+    // generate the amberc configuration out of the given target properties
+    var configuration = generateCompilerConfiguration(this.data, grunt.config('amberc._config.amber_dir'));
+
+    // run the compiler
+    // change back to the old working directory and call the async callback once finished
+    var self = this;
+    compiler.main(configuration, function(){
+      if (undefined !== self.data.target_dir) {
+        var absolute_target_dir = path.join(current_dir, self.data.target_dir);
+        replaceFileSuffix_moveToTargetDir(self.data.src, absolute_target_dir);
+        // if deploy is set also copy the deploy files
+        if (self.data.deploy) {
+          var suffix = self.data.output_suffix || 'deploy';
+          suffix = '.' + suffix + '.js';
+          replaceFileSuffix_moveToTargetDir(self.data.src, absolute_target_dir, suffix);
+        }
+      }
+      // reset working directory
+      grunt.file.setBase(current_dir);
+      // signal that task has finished
+      done();
+    });
+  });
+
+
+  function generateCompilerConfiguration(data, amber_dir) {
+    var configuration = amberc.createDefaults(amber_dir);
+    var parameters = [];
+
+    var libraries = data.libraries;
+    if (undefined !== libraries) {
+      configuration.load = libraries;
+    }
+    var initFile = data.init;
+    if (undefined !== initFile) {
+      configuration.init = initFile;
+    }
+    var mainClass = data.main_class;
+    if (undefined !== mainClass) {
+      configuration.main = mainClass;
+    }
+    var mainFile = data.main_file;
+    if (undefined !== initFile) {
+      configuration.mainfile = mainFile;
+    }
+    if (true === data.deploy) {
+      configuration.deploy = true;
+    }
+    var outputSuffix = data.output_suffix;
+    if (undefined !== outputSuffix) {
+      configuration.suffix = outputSuffix;
+      configuration.suffix_used = outputSuffix;
+    }
+    var librarySuffix = data.library_suffix;
+    if (undefined !== librarySuffix) {
+      configuration.loadsuffix = librarySuffix;
+      configuration.suffix_used = librarySuffix;
+    }
+    var sourceFiles = data.src;
+    if (undefined !== sourceFiles) {
+      sourceFiles.forEach(function(currentItem){
+        var fileSuffix = path.extname(currentItem);
+        switch (fileSuffix) {
+          case '.st':
+            configuration.stFiles.push(currentItem);
+          break;
+          case '.js':
+            configuration.jsFiles.push(currentItem);
+          break;
+        }
+      });
+    }
+    var outputName = data.output_name;
+    if (undefined !== outputName) {
+      configuration.program = outputName;
+    }
+    return configuration;
+  }
+
+
+  /**
+   * Replace '.st' suffix of \p files with '.js' or with \p replace_suffix if this parameter is given.
+   * Afterwards move the files with replaced suffix to \p target_dir if the files exist.
+   */
+  function replaceFileSuffix_moveToTargetDir(files, target_dir, replace_suffix) {
+    var suffix = replace_suffix || '.js';
+    var compiledFiles = files.map(function(item) {
+      return item.replace(/.st$/g, suffix);
+    });
+
+    compiledFiles.forEach(function(file) {
+      if (path.existsSync(file)) {
+        console.log('Move: ' + file + ' -> ' + path.join(target_dir, file));
+        fs.renameSync(file, path.join(target_dir, file));
+      }
+    });
+  }
+};

二进制
images/sprite.amber.png


+ 16 - 1
js/Canvas.deploy.js

@@ -1736,12 +1736,27 @@ smalltalk.method({
 selector: "initialize",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
smalltalk.Object.klass.fn.prototype._initialize.apply(_st(self), []);
+return smalltalk.withContext(function($ctx1) { 
var $1;
+smalltalk.Object.klass.fn.prototype._initialize.apply(_st(self), []);
+$1=_st(self)._isDOMAvailable();
+if(smalltalk.assert($1)){
 _st(self)._ensureCurrent();
+};
 return self}, function($ctx1) {$ctx1.fill(self,"initialize",{}, smalltalk.HTMLSnippet.klass)})}
 }),
 smalltalk.HTMLSnippet.klass);
 
+smalltalk.addMethod(
+"_isDOMAvailable",
+smalltalk.method({
+selector: "isDOMAvailable",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
 return typeof document !== 'undefined' ;
+return self}, function($ctx1) {$ctx1.fill(self,"isDOMAvailable",{}, smalltalk.HTMLSnippet.klass)})}
+}),
+smalltalk.HTMLSnippet.klass);
+
 smalltalk.addMethod(
 "_new",
 smalltalk.method({

+ 25 - 5
js/Canvas.js

@@ -2186,7 +2186,7 @@ smalltalk.HTMLCanvas.klass);
 
 
 smalltalk.addClass('HTMLSnippet', smalltalk.Object, ['snippets'], 'Canvas');
-smalltalk.HTMLSnippet.comment="HTMLSnippet instance is the registry of html snippets.\x0aHTMLSnippet current is the public singleton instance.\x0a\x0aAt the beginning, it scans the document for any html elements\x0awith 'data-snippet=\x22foo\x22' attribute and takes them off the document,\x0aremembering them in the store under the specified name.\x0aIt also install method #foo into HTMLCanvas dynamically.\x0a\x0aEvery html snippet should mark a 'caret', a place where contents\x0acan be inserted, by 'data-snippet=\x22*\x22' (a special name for caret).\x0aFor example:\x0a\x0a<li data-snippet='menuelement' class='...'><a data-snippet='*'></a></li>\x0a\x0adefines a list element with a link inside; the link itself is marked as a caret.\x0a\x0aYou can later issue\x0a\x0ahtml menuelement href: '/foo'; with: 'A foo'\x0a\x0ato insert the whole snippet and directly manipulate the caret, so it renders:\x0a\x0a<li class='...'><a href='/foo'>A foo</a></li>\x0a\x0aFor a self-careting tags (not very useful, but you do not need to fill class etc.\x0ayou can use\x0a\x0a<div class='lots of classes' attr1='one' attr2='two' data-snippet='*bar'></div>\x0a\x0aand in code later do:\x0a\x0ahtml bar with: [ xxx ]\x0a\x0ato render\x0a\x0a<div class='lots of classes' attr1='one' attr2='two'>...added by xxx...</div>\x0a"
+smalltalk.HTMLSnippet.comment="HTMLSnippet instance is the registry of html snippets.\x0aHTMLSnippet current is the public singleton instance.\x0a\x0aAt the beginning, it scans the document for any html elements\x0awith 'data-snippet=\x22foo\x22' attribute and takes them off the document,\x0aremembering them in the store under the specified name.\x0aIt also install method #foo into HTMLCanvas dynamically.\x0a\x0aEvery html snippet should mark a 'caret', a place where contents\x0acan be inserted, by 'data-snippet=\x22*\x22' (a special name for caret).\x0aFor example:\x0a\x0a<li data-snippet='menuelement' class='...'><a data-snippet='*'></a></li>\x0a\x0adefines a list element with a link inside; the link itself is marked as a caret.\x0a\x0aYou can later issue\x0a\x0ahtml menuelement href: '/foo'; with: 'A foo'\x0a\x0ato insert the whole snippet and directly manipulate the caret, so it renders:\x0a\x0a<li class='...'><a href='/foo'>A foo</a></li>\x0a\x0aFor a self-careting tags (not very useful, but you do not need to fill class etc.\x0ayou can use\x0a\x0a<div class='lots of classes' attr1='one' attr2='two' data-snippet='*bar'></div>\x0a\x0aand in code later do:\x0a\x0ahtml bar with: [ xxx ]\x0a\x0ato render\x0a\x0a<div class='lots of classes' attr1='one' attr2='two'>...added by xxx...</div>"
 smalltalk.addMethod(
 "_initializeFromJQuery_",
 smalltalk.method({
@@ -2306,7 +2306,7 @@ $1=$2;
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"snippets",{}, smalltalk.HTMLSnippet)})},
 args: [],
-source: "snippets\x0a\x09^snippets ifNil: [ snippets := #{} ]\x0a",
+source: "snippets\x0a\x09^snippets ifNil: [ snippets := #{} ]",
 messageSends: ["ifNil:"],
 referencedClasses: []
 }),
@@ -2383,12 +2383,32 @@ selector: "initialize",
 category: 'initialization',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
smalltalk.Object.klass.fn.prototype._initialize.apply(_st(self), []);
+return smalltalk.withContext(function($ctx1) { 
var $1;
+smalltalk.Object.klass.fn.prototype._initialize.apply(_st(self), []);
+$1=_st(self)._isDOMAvailable();
+if(smalltalk.assert($1)){
 _st(self)._ensureCurrent();
+};
 return self}, function($ctx1) {$ctx1.fill(self,"initialize",{}, smalltalk.HTMLSnippet.klass)})},
 args: [],
-source: "initialize\x0a\x09super initialize.\x0a\x09self ensureCurrent",
-messageSends: ["initialize", "ensureCurrent"],
+source: "initialize\x0a\x09super initialize.\x0a    self isDOMAvailable ifTrue: [\x0a\x09\x09self ensureCurrent ]",
+messageSends: ["initialize", "ifTrue:", "ensureCurrent", "isDOMAvailable"],
+referencedClasses: []
+}),
+smalltalk.HTMLSnippet.klass);
+
+smalltalk.addMethod(
+"_isDOMAvailable",
+smalltalk.method({
+selector: "isDOMAvailable",
+category: 'instance creation',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
 return typeof document !== 'undefined' ;
+return self}, function($ctx1) {$ctx1.fill(self,"isDOMAvailable",{}, smalltalk.HTMLSnippet.klass)})},
+args: [],
+source: "isDOMAvailable\x0a\x09< return typeof document !== 'undefined' >",
+messageSends: [],
 referencedClasses: []
 }),
 smalltalk.HTMLSnippet.klass);

+ 9 - 11
js/Compiler-Core.deploy.js

@@ -537,20 +537,18 @@ smalltalk.method({
 selector: "classNameFor:",
 fn: function (aClass){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $2,$3,$5,$4,$1;
+return smalltalk.withContext(function($ctx1) { 
var $2,$3,$1;
 $2=_st(aClass)._isMetaclass();
-$3=(function(){
-return smalltalk.withContext(function($ctx2) {
return _st(_st(_st(aClass)._instanceClass())._name()).__comma(".klass");
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-$4=(function(){
-return smalltalk.withContext(function($ctx2) {
$5=_st(aClass)._isNil();
-if(smalltalk.assert($5)){
-return "nil";
+if(smalltalk.assert($2)){
+$1=_st(_st(_st(aClass)._instanceClass())._name()).__comma(".klass");
 } else {
-return _st(aClass)._name();
+$3=_st(aClass)._isNil();
+if(smalltalk.assert($3)){
+$1="nil";
+} else {
+$1=_st(aClass)._name();
+};
 };
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-$1=_st($2)._ifTrue_ifFalse_($3,$4);
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"classNameFor:",{aClass:aClass}, smalltalk.AbstractCodeGenerator)})}
 }),

+ 9 - 11
js/Compiler-Core.js

@@ -732,20 +732,18 @@ selector: "classNameFor:",
 category: 'accessing',
 fn: function (aClass){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $2,$3,$5,$4,$1;
+return smalltalk.withContext(function($ctx1) { 
var $2,$3,$1;
 $2=_st(aClass)._isMetaclass();
-$3=(function(){
-return smalltalk.withContext(function($ctx2) {
return _st(_st(_st(aClass)._instanceClass())._name()).__comma(".klass");
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-$4=(function(){
-return smalltalk.withContext(function($ctx2) {
$5=_st(aClass)._isNil();
-if(smalltalk.assert($5)){
-return "nil";
+if(smalltalk.assert($2)){
+$1=_st(_st(_st(aClass)._instanceClass())._name()).__comma(".klass");
 } else {
-return _st(aClass)._name();
+$3=_st(aClass)._isNil();
+if(smalltalk.assert($3)){
+$1="nil";
+} else {
+$1=_st(aClass)._name();
+};
 };
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-$1=_st($2)._ifTrue_ifFalse_($3,$4);
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"classNameFor:",{aClass:aClass}, smalltalk.AbstractCodeGenerator)})},
 args: ["aClass"],

+ 26 - 0
js/Compiler-Exceptions.deploy.js

@@ -131,3 +131,29 @@ smalltalk.UnknownVariableError);
 
 
 
+smalltalk.addClass('RethrowErrorHandler', smalltalk.ErrorHandler, [], 'Compiler-Exceptions');
+smalltalk.addMethod(
+"_basicSignal_",
+smalltalk.method({
+selector: "basicSignal:",
+fn: function (anError){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
throw anError;
+return self}, function($ctx1) {$ctx1.fill(self,"basicSignal:",{anError:anError}, smalltalk.RethrowErrorHandler)})}
+}),
+smalltalk.RethrowErrorHandler);
+
+smalltalk.addMethod(
+"_handleError_",
+smalltalk.method({
+selector: "handleError:",
+fn: function (anError){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
smalltalk.ErrorHandler.fn.prototype._handleError_.apply(_st(self), [anError]);
+_st(self)._basicSignal_(anError);
+return self}, function($ctx1) {$ctx1.fill(self,"handleError:",{anError:anError}, smalltalk.RethrowErrorHandler)})}
+}),
+smalltalk.RethrowErrorHandler);
+
+
+

+ 37 - 0
js/Compiler-Exceptions.js

@@ -183,3 +183,40 @@ smalltalk.UnknownVariableError);
 
 
 
+smalltalk.addClass('RethrowErrorHandler', smalltalk.ErrorHandler, [], 'Compiler-Exceptions');
+smalltalk.RethrowErrorHandler.comment="This class is used in the commandline version of the compiler.\x0aIt uses the handleError: message of ErrorHandler for printing the stacktrace and throws the error again as JS exception.\x0aAs a result Smalltalk errors are not swallowd by the Amber runtime and compilation can be aborted."
+smalltalk.addMethod(
+"_basicSignal_",
+smalltalk.method({
+selector: "basicSignal:",
+category: 'error handling',
+fn: function (anError){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
throw anError;
+return self}, function($ctx1) {$ctx1.fill(self,"basicSignal:",{anError:anError}, smalltalk.RethrowErrorHandler)})},
+args: ["anError"],
+source: "basicSignal: anError\x0a\x09<throw anError>",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.RethrowErrorHandler);
+
+smalltalk.addMethod(
+"_handleError_",
+smalltalk.method({
+selector: "handleError:",
+category: 'error handling',
+fn: function (anError){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
smalltalk.ErrorHandler.fn.prototype._handleError_.apply(_st(self), [anError]);
+_st(self)._basicSignal_(anError);
+return self}, function($ctx1) {$ctx1.fill(self,"handleError:",{anError:anError}, smalltalk.RethrowErrorHandler)})},
+args: ["anError"],
+source: "handleError: anError\x0a\x09super handleError: anError.\x0a    self basicSignal: anError",
+messageSends: ["handleError:", "basicSignal:"],
+referencedClasses: []
+}),
+smalltalk.RethrowErrorHandler);
+
+
+

+ 44 - 66
js/Compiler-IR.deploy.js

@@ -36,32 +36,28 @@ selector: "aliasTemporally:",
 fn: function (aCollection){
 var self=this;
 var threshold,result;
-return smalltalk.withContext(function($ctx1) { 
var $1,$3,$2,$4,$6,$8,$7,$5,$9;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2,$4,$3,$5;
 threshold=(0);
-$1=aCollection;
-$2=(function(each,i){
-return smalltalk.withContext(function($ctx2) {
$3=_st(each)._subtreeNeedsAliasing();
-if(smalltalk.assert($3)){
+_st(aCollection)._withIndexDo_((function(each,i){
+return smalltalk.withContext(function($ctx2) {
$1=_st(each)._subtreeNeedsAliasing();
+if(smalltalk.assert($1)){
 threshold=i;
 return threshold;
 };
-}, function($ctx2) {$ctx2.fillBlock({each:each,i:i},$ctx1)})});
-_st($1)._withIndexDo_($2);
+}, function($ctx2) {$ctx2.fillBlock({each:each,i:i},$ctx1)})}));
 result=_st((smalltalk.OrderedCollection || OrderedCollection))._new();
-$4=aCollection;
-$5=(function(each,i){
-return smalltalk.withContext(function($ctx2) {
$6=result;
-$8=_st(i).__lt_eq(threshold);
-if(smalltalk.assert($8)){
-$7=_st(self)._alias_(each);
+_st(aCollection)._withIndexDo_((function(each,i){
+return smalltalk.withContext(function($ctx2) {
$2=result;
+$4=_st(i).__lt_eq(threshold);
+if(smalltalk.assert($4)){
+$3=_st(self)._alias_(each);
 } else {
-$7=_st(self)._visit_(each);
+$3=_st(self)._visit_(each);
 };
-return _st($6)._add_($7);
-}, function($ctx2) {$ctx2.fillBlock({each:each,i:i},$ctx1)})});
-_st($4)._withIndexDo_($5);
-$9=result;
-return $9;
+return _st($2)._add_($3);
+}, function($ctx2) {$ctx2.fillBlock({each:each,i:i},$ctx1)})}));
+$5=result;
+return $5;
 }, function($ctx1) {$ctx1.fill(self,"aliasTemporally:",{aCollection:aCollection,threshold:threshold,result:result}, smalltalk.IRASTTranslator)})}
 }),
 smalltalk.IRASTTranslator);
@@ -239,28 +235,23 @@ smalltalk.method({
 selector: "visitBlockSequenceNode:",
 fn: function (aNode){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $2,$3,$5,$7,$8,$9,$6,$4,$1;
-$2=self;
-$3=_st((smalltalk.IRBlockSequence || IRBlockSequence))._new();
-$4=(function(){
-return smalltalk.withContext(function($ctx2) {
$5=_st(aNode)._nodes();
-$6=(function(){
+return smalltalk.withContext(function($ctx1) { 
var $2,$3,$4,$1;
+$1=_st(self)._withSequence_do_(_st((smalltalk.IRBlockSequence || IRBlockSequence))._new(),(function(){
+return smalltalk.withContext(function($ctx2) {
return _st(_st(aNode)._nodes())._ifNotEmpty_((function(){
 return smalltalk.withContext(function($ctx3) {
_st(_st(_st(aNode)._nodes())._allButLast())._do_((function(each){
 return smalltalk.withContext(function($ctx4) {
return _st(_st(self)._sequence())._add_(_st(self)._visit_(each));
 }, function($ctx4) {$ctx4.fillBlock({each:each},$ctx1)})}));
-$7=_st(_st(_st(aNode)._nodes())._last())._isReturnNode();
-if(smalltalk.assert($7)){
+$2=_st(_st(_st(aNode)._nodes())._last())._isReturnNode();
+if(smalltalk.assert($2)){
 return _st(_st(self)._sequence())._add_(_st(self)._visit_(_st(_st(aNode)._nodes())._last()));
 } else {
-$8=_st((smalltalk.IRBlockReturn || IRBlockReturn))._new();
-_st($8)._add_(_st(self)._visit_(_st(_st(aNode)._nodes())._last()));
-$9=_st($8)._yourself();
-return _st(_st(self)._sequence())._add_($9);
+$3=_st((smalltalk.IRBlockReturn || IRBlockReturn))._new();
+_st($3)._add_(_st(self)._visit_(_st(_st(aNode)._nodes())._last()));
+$4=_st($3)._yourself();
+return _st(_st(self)._sequence())._add_($4);
 };
-}, function($ctx3) {$ctx3.fillBlock({},$ctx1)})});
-return _st($5)._ifNotEmpty_($6);
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-$1=_st($2)._withSequence_do_($3,$4);
+}, function($ctx3) {$ctx3.fillBlock({},$ctx1)})}));
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"visitBlockSequenceNode:",{aNode:aNode}, smalltalk.IRASTTranslator)})}
 }),
@@ -443,23 +434,18 @@ smalltalk.method({
 selector: "visitSequenceNode:",
 fn: function (aNode){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $2,$3,$5,$7,$6,$4,$1;
-$2=self;
-$3=_st((smalltalk.IRSequence || IRSequence))._new();
-$4=(function(){
-return smalltalk.withContext(function($ctx2) {
$5=_st(aNode)._nodes();
-$6=(function(each){
+return smalltalk.withContext(function($ctx1) { 
var $2,$1;
+$1=_st(self)._withSequence_do_(_st((smalltalk.IRSequence || IRSequence))._new(),(function(){
+return smalltalk.withContext(function($ctx2) {
return _st(_st(aNode)._nodes())._do_((function(each){
 var instruction;
 return smalltalk.withContext(function($ctx3) {
instruction=_st(self)._visit_(each);
 instruction;
-$7=_st(instruction)._isVariable();
-if(! smalltalk.assert($7)){
+$2=_st(instruction)._isVariable();
+if(! smalltalk.assert($2)){
 return _st(_st(self)._sequence())._add_(instruction);
 };
-}, function($ctx3) {$ctx3.fillBlock({each:each,instruction:instruction},$ctx1)})});
-return _st($5)._do_($6);
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-$1=_st($2)._withSequence_do_($3,$4);
+}, function($ctx3) {$ctx3.fillBlock({each:each,instruction:instruction},$ctx1)})}));
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"visitSequenceNode:",{aNode:aNode}, smalltalk.IRASTTranslator)})}
 }),
@@ -2025,38 +2011,30 @@ smalltalk.method({
 selector: "visitIRMethod:",
 fn: function (anIRMethod){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$2,$4,$6,$7,$9,$10,$8,$5,$3;
-$1=_st(self)._stream();
-$2=anIRMethod;
-$3=(function(){
-return smalltalk.withContext(function($ctx2) {
$4=_st(self)._stream();
-$5=(function(){
+return smalltalk.withContext(function($ctx1) { 
var $1,$2;
+_st(_st(self)._stream())._nextPutMethodDeclaration_with_(anIRMethod,(function(){
+return smalltalk.withContext(function($ctx2) {
return _st(_st(self)._stream())._nextPutFunctionWith_arguments_((function(){
 return smalltalk.withContext(function($ctx3) {
_st(_st(self)._stream())._nextPutVars_(_st(_st(anIRMethod)._tempDeclarations())._collect_((function(each){
 return smalltalk.withContext(function($ctx4) {
return _st(_st(each)._name())._asVariableName();
 }, function($ctx4) {$ctx4.fillBlock({each:each},$ctx1)})})));
-$6=_st(self)._stream();
-$7=anIRMethod;
-$8=(function(){
-return smalltalk.withContext(function($ctx4) {
$9=_st(_st(anIRMethod)._internalVariables())._notEmpty();
-if(smalltalk.assert($9)){
+return _st(_st(self)._stream())._nextPutContextFor_during_(anIRMethod,(function(){
+return smalltalk.withContext(function($ctx4) {
$1=_st(_st(anIRMethod)._internalVariables())._notEmpty();
+if(smalltalk.assert($1)){
 _st(_st(self)._stream())._nextPutVars_(_st(_st(_st(anIRMethod)._internalVariables())._asArray())._collect_((function(each){
 return smalltalk.withContext(function($ctx5) {
return _st(_st(each)._variable())._alias();
 }, function($ctx5) {$ctx5.fillBlock({each:each},$ctx1)})})));
 };
-$10=_st(_st(anIRMethod)._scope())._hasNonLocalReturn();
-if(smalltalk.assert($10)){
+$2=_st(_st(anIRMethod)._scope())._hasNonLocalReturn();
+if(smalltalk.assert($2)){
 return _st(_st(self)._stream())._nextPutNonLocalReturnHandlingWith_((function(){
 return smalltalk.withContext(function($ctx5) {
return smalltalk.IRVisitor.fn.prototype._visitIRMethod_.apply(_st(self), [anIRMethod]);
 }, function($ctx5) {$ctx5.fillBlock({},$ctx1)})}));
 } else {
 return smalltalk.IRVisitor.fn.prototype._visitIRMethod_.apply(_st(self), [anIRMethod]);
 };
-}, function($ctx4) {$ctx4.fillBlock({},$ctx1)})});
-return _st($6)._nextPutContextFor_during_($7,$8);
-}, function($ctx3) {$ctx3.fillBlock({},$ctx1)})});
-return _st($4)._nextPutFunctionWith_arguments_($5,_st(anIRMethod)._arguments());
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-_st($1)._nextPutMethodDeclaration_with_($2,$3);
+}, function($ctx4) {$ctx4.fillBlock({},$ctx1)})}));
+}, function($ctx3) {$ctx3.fillBlock({},$ctx1)})}),_st(anIRMethod)._arguments());
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
 return self}, function($ctx1) {$ctx1.fill(self,"visitIRMethod:",{anIRMethod:anIRMethod}, smalltalk.IRJSTranslator)})}
 }),
 smalltalk.IRJSTranslator);

+ 44 - 66
js/Compiler-IR.js

@@ -43,32 +43,28 @@ category: 'visiting',
 fn: function (aCollection){
 var self=this;
 var threshold,result;
-return smalltalk.withContext(function($ctx1) { 
var $1,$3,$2,$4,$6,$8,$7,$5,$9;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2,$4,$3,$5;
 threshold=(0);
-$1=aCollection;
-$2=(function(each,i){
-return smalltalk.withContext(function($ctx2) {
$3=_st(each)._subtreeNeedsAliasing();
-if(smalltalk.assert($3)){
+_st(aCollection)._withIndexDo_((function(each,i){
+return smalltalk.withContext(function($ctx2) {
$1=_st(each)._subtreeNeedsAliasing();
+if(smalltalk.assert($1)){
 threshold=i;
 return threshold;
 };
-}, function($ctx2) {$ctx2.fillBlock({each:each,i:i},$ctx1)})});
-_st($1)._withIndexDo_($2);
+}, function($ctx2) {$ctx2.fillBlock({each:each,i:i},$ctx1)})}));
 result=_st((smalltalk.OrderedCollection || OrderedCollection))._new();
-$4=aCollection;
-$5=(function(each,i){
-return smalltalk.withContext(function($ctx2) {
$6=result;
-$8=_st(i).__lt_eq(threshold);
-if(smalltalk.assert($8)){
-$7=_st(self)._alias_(each);
+_st(aCollection)._withIndexDo_((function(each,i){
+return smalltalk.withContext(function($ctx2) {
$2=result;
+$4=_st(i).__lt_eq(threshold);
+if(smalltalk.assert($4)){
+$3=_st(self)._alias_(each);
 } else {
-$7=_st(self)._visit_(each);
+$3=_st(self)._visit_(each);
 };
-return _st($6)._add_($7);
-}, function($ctx2) {$ctx2.fillBlock({each:each,i:i},$ctx1)})});
-_st($4)._withIndexDo_($5);
-$9=result;
-return $9;
+return _st($2)._add_($3);
+}, function($ctx2) {$ctx2.fillBlock({each:each,i:i},$ctx1)})}));
+$5=result;
+return $5;
 }, function($ctx1) {$ctx1.fill(self,"aliasTemporally:",{aCollection:aCollection,threshold:threshold,result:result}, smalltalk.IRASTTranslator)})},
 args: ["aCollection"],
 source: "aliasTemporally: aCollection\x0a\x09\x22https://github.com/NicolasPetton/amber/issues/296\x0a    \x0a    If a node is aliased, all preceding ones are aliased as well.\x0a    The tree is iterated twice. First we get the aliasing dependency, \x0a    then the aliasing itself is done\x22\x0a\x0a\x09| threshold result |\x0a    threshold := 0.\x0a    \x0a    aCollection withIndexDo: [ :each :i |\x0a        each subtreeNeedsAliasing\x0a\x09\x09    ifTrue: [ threshold := i ]].\x0a\x0a\x09result := OrderedCollection new.\x0a\x09aCollection withIndexDo: [ :each :i | \x0a\x09\x09result add: (i <= threshold\x0a\x09\x09\x09ifTrue: [ self alias: each ]\x0a\x09\x09\x09ifFalse: [ self visit: each ])].\x0a\x0a    ^result",
@@ -306,28 +302,23 @@ selector: "visitBlockSequenceNode:",
 category: 'visiting',
 fn: function (aNode){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $2,$3,$5,$7,$8,$9,$6,$4,$1;
-$2=self;
-$3=_st((smalltalk.IRBlockSequence || IRBlockSequence))._new();
-$4=(function(){
-return smalltalk.withContext(function($ctx2) {
$5=_st(aNode)._nodes();
-$6=(function(){
+return smalltalk.withContext(function($ctx1) { 
var $2,$3,$4,$1;
+$1=_st(self)._withSequence_do_(_st((smalltalk.IRBlockSequence || IRBlockSequence))._new(),(function(){
+return smalltalk.withContext(function($ctx2) {
return _st(_st(aNode)._nodes())._ifNotEmpty_((function(){
 return smalltalk.withContext(function($ctx3) {
_st(_st(_st(aNode)._nodes())._allButLast())._do_((function(each){
 return smalltalk.withContext(function($ctx4) {
return _st(_st(self)._sequence())._add_(_st(self)._visit_(each));
 }, function($ctx4) {$ctx4.fillBlock({each:each},$ctx1)})}));
-$7=_st(_st(_st(aNode)._nodes())._last())._isReturnNode();
-if(smalltalk.assert($7)){
+$2=_st(_st(_st(aNode)._nodes())._last())._isReturnNode();
+if(smalltalk.assert($2)){
 return _st(_st(self)._sequence())._add_(_st(self)._visit_(_st(_st(aNode)._nodes())._last()));
 } else {
-$8=_st((smalltalk.IRBlockReturn || IRBlockReturn))._new();
-_st($8)._add_(_st(self)._visit_(_st(_st(aNode)._nodes())._last()));
-$9=_st($8)._yourself();
-return _st(_st(self)._sequence())._add_($9);
+$3=_st((smalltalk.IRBlockReturn || IRBlockReturn))._new();
+_st($3)._add_(_st(self)._visit_(_st(_st(aNode)._nodes())._last()));
+$4=_st($3)._yourself();
+return _st(_st(self)._sequence())._add_($4);
 };
-}, function($ctx3) {$ctx3.fillBlock({},$ctx1)})});
-return _st($5)._ifNotEmpty_($6);
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-$1=_st($2)._withSequence_do_($3,$4);
+}, function($ctx3) {$ctx3.fillBlock({},$ctx1)})}));
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"visitBlockSequenceNode:",{aNode:aNode}, smalltalk.IRASTTranslator)})},
 args: ["aNode"],
@@ -550,23 +541,18 @@ selector: "visitSequenceNode:",
 category: 'visiting',
 fn: function (aNode){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $2,$3,$5,$7,$6,$4,$1;
-$2=self;
-$3=_st((smalltalk.IRSequence || IRSequence))._new();
-$4=(function(){
-return smalltalk.withContext(function($ctx2) {
$5=_st(aNode)._nodes();
-$6=(function(each){
+return smalltalk.withContext(function($ctx1) { 
var $2,$1;
+$1=_st(self)._withSequence_do_(_st((smalltalk.IRSequence || IRSequence))._new(),(function(){
+return smalltalk.withContext(function($ctx2) {
return _st(_st(aNode)._nodes())._do_((function(each){
 var instruction;
 return smalltalk.withContext(function($ctx3) {
instruction=_st(self)._visit_(each);
 instruction;
-$7=_st(instruction)._isVariable();
-if(! smalltalk.assert($7)){
+$2=_st(instruction)._isVariable();
+if(! smalltalk.assert($2)){
 return _st(_st(self)._sequence())._add_(instruction);
 };
-}, function($ctx3) {$ctx3.fillBlock({each:each,instruction:instruction},$ctx1)})});
-return _st($5)._do_($6);
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-$1=_st($2)._withSequence_do_($3,$4);
+}, function($ctx3) {$ctx3.fillBlock({each:each,instruction:instruction},$ctx1)})}));
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"visitSequenceNode:",{aNode:aNode}, smalltalk.IRASTTranslator)})},
 args: ["aNode"],
@@ -2730,38 +2716,30 @@ selector: "visitIRMethod:",
 category: 'visiting',
 fn: function (anIRMethod){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$2,$4,$6,$7,$9,$10,$8,$5,$3;
-$1=_st(self)._stream();
-$2=anIRMethod;
-$3=(function(){
-return smalltalk.withContext(function($ctx2) {
$4=_st(self)._stream();
-$5=(function(){
+return smalltalk.withContext(function($ctx1) { 
var $1,$2;
+_st(_st(self)._stream())._nextPutMethodDeclaration_with_(anIRMethod,(function(){
+return smalltalk.withContext(function($ctx2) {
return _st(_st(self)._stream())._nextPutFunctionWith_arguments_((function(){
 return smalltalk.withContext(function($ctx3) {
_st(_st(self)._stream())._nextPutVars_(_st(_st(anIRMethod)._tempDeclarations())._collect_((function(each){
 return smalltalk.withContext(function($ctx4) {
return _st(_st(each)._name())._asVariableName();
 }, function($ctx4) {$ctx4.fillBlock({each:each},$ctx1)})})));
-$6=_st(self)._stream();
-$7=anIRMethod;
-$8=(function(){
-return smalltalk.withContext(function($ctx4) {
$9=_st(_st(anIRMethod)._internalVariables())._notEmpty();
-if(smalltalk.assert($9)){
+return _st(_st(self)._stream())._nextPutContextFor_during_(anIRMethod,(function(){
+return smalltalk.withContext(function($ctx4) {
$1=_st(_st(anIRMethod)._internalVariables())._notEmpty();
+if(smalltalk.assert($1)){
 _st(_st(self)._stream())._nextPutVars_(_st(_st(_st(anIRMethod)._internalVariables())._asArray())._collect_((function(each){
 return smalltalk.withContext(function($ctx5) {
return _st(_st(each)._variable())._alias();
 }, function($ctx5) {$ctx5.fillBlock({each:each},$ctx1)})})));
 };
-$10=_st(_st(anIRMethod)._scope())._hasNonLocalReturn();
-if(smalltalk.assert($10)){
+$2=_st(_st(anIRMethod)._scope())._hasNonLocalReturn();
+if(smalltalk.assert($2)){
 return _st(_st(self)._stream())._nextPutNonLocalReturnHandlingWith_((function(){
 return smalltalk.withContext(function($ctx5) {
return smalltalk.IRVisitor.fn.prototype._visitIRMethod_.apply(_st(self), [anIRMethod]);
 }, function($ctx5) {$ctx5.fillBlock({},$ctx1)})}));
 } else {
 return smalltalk.IRVisitor.fn.prototype._visitIRMethod_.apply(_st(self), [anIRMethod]);
 };
-}, function($ctx4) {$ctx4.fillBlock({},$ctx1)})});
-return _st($6)._nextPutContextFor_during_($7,$8);
-}, function($ctx3) {$ctx3.fillBlock({},$ctx1)})});
-return _st($4)._nextPutFunctionWith_arguments_($5,_st(anIRMethod)._arguments());
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-_st($1)._nextPutMethodDeclaration_with_($2,$3);
+}, function($ctx4) {$ctx4.fillBlock({},$ctx1)})}));
+}, function($ctx3) {$ctx3.fillBlock({},$ctx1)})}),_st(anIRMethod)._arguments());
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
 return self}, function($ctx1) {$ctx1.fill(self,"visitIRMethod:",{anIRMethod:anIRMethod}, smalltalk.IRJSTranslator)})},
 args: ["anIRMethod"],
 source: "visitIRMethod: anIRMethod\x0a\x0a\x09self stream\x0a\x09\x09nextPutMethodDeclaration: anIRMethod \x0a\x09\x09with: [ self stream \x0a\x09\x09\x09nextPutFunctionWith: [ \x0a            \x09self stream nextPutVars: (anIRMethod tempDeclarations collect: [ :each |\x0a    \x09\x09\x09\x09each name asVariableName ]).\x0a            \x09self stream nextPutContextFor: anIRMethod during: [\x0a\x09\x09\x09\x09anIRMethod internalVariables notEmpty ifTrue: [\x0a\x09\x09\x09\x09\x09self stream nextPutVars: (anIRMethod internalVariables asArray collect: [ :each |\x0a\x09\x09\x09\x09\x09\x09each variable alias ]) ].\x0a\x09\x09\x09\x09anIRMethod scope hasNonLocalReturn \x0a\x09\x09\x09\x09\x09ifTrue: [\x0a\x09\x09\x09\x09\x09\x09self stream nextPutNonLocalReturnHandlingWith: [\x0a\x09\x09\x09\x09\x09\x09\x09super visitIRMethod: anIRMethod ]]\x0a\x09\x09\x09\x09\x09ifFalse: [ super visitIRMethod: anIRMethod ]]]\x0a\x09\x09\x09arguments: anIRMethod arguments ]",

+ 39 - 49
js/Compiler-Inlining.deploy.js

@@ -711,37 +711,33 @@ selector: "inlineClosure:",
 fn: function (anIRClosure){
 var self=this;
 var inlinedClosure,sequence,statements;
-return smalltalk.withContext(function($ctx1) { 
var $1,$3,$2,$4,$6,$5,$7;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3;
 inlinedClosure=_st(self)._inlinedClosure();
 _st(inlinedClosure)._scope_(_st(anIRClosure)._scope());
-$1=_st(anIRClosure)._instructions();
-$2=(function(each){
-return smalltalk.withContext(function($ctx2) {
$3=_st(each)._isSequence();
-if(! smalltalk.assert($3)){
+_st(_st(anIRClosure)._instructions())._do_((function(each){
+return smalltalk.withContext(function($ctx2) {
$1=_st(each)._isSequence();
+if(! smalltalk.assert($1)){
 return _st(inlinedClosure)._add_(each);
 };
-}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})});
-_st($1)._do_($2);
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
 sequence=_st(self)._inlinedSequence();
 _st(inlinedClosure)._add_(sequence);
 statements=_st(_st(_st(anIRClosure)._instructions())._last())._instructions();
-$4=statements;
-$5=(function(){
+_st(statements)._ifNotEmpty_((function(){
 return smalltalk.withContext(function($ctx2) {
_st(_st(statements)._allButLast())._do_((function(each){
 return smalltalk.withContext(function($ctx3) {
return _st(sequence)._add_(each);
 }, function($ctx3) {$ctx3.fillBlock({each:each},$ctx1)})}));
-$6=_st(_st(_st(statements)._last())._isReturn())._and_((function(){
+$2=_st(_st(_st(statements)._last())._isReturn())._and_((function(){
 return smalltalk.withContext(function($ctx3) {
return _st(_st(statements)._last())._isBlockReturn();
 }, function($ctx3) {$ctx3.fillBlock({},$ctx1)})}));
-if(smalltalk.assert($6)){
+if(smalltalk.assert($2)){
 return _st(sequence)._add_(_st(_st(_st(statements)._last())._instructions())._first());
 } else {
 return _st(sequence)._add_(_st(statements)._last());
 };
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-_st($4)._ifNotEmpty_($5);
-$7=inlinedClosure;
-return $7;
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
+$3=inlinedClosure;
+return $3;
 }, function($ctx1) {$ctx1.fill(self,"inlineClosure:",{anIRClosure:anIRClosure,inlinedClosure:inlinedClosure,sequence:sequence,statements:statements}, smalltalk.IRSendInliner)})}
 }),
 smalltalk.IRSendInliner);
@@ -927,21 +923,19 @@ smalltalk.method({
 selector: "shouldInline:",
 fn: function (anIRInstruction){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$2,$4,$3;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2;
 var $early={};
 try {
 $1=_st(_st(self)._inlinedSelectors())._includes_(_st(anIRInstruction)._selector());
 if(! smalltalk.assert($1)){
 return false;
 };
-$2=_st(_st(anIRInstruction)._instructions())._allButFirst();
-$3=(function(each){
-return smalltalk.withContext(function($ctx2) {
$4=_st(each)._isClosure();
-if(! smalltalk.assert($4)){
+_st(_st(_st(anIRInstruction)._instructions())._allButFirst())._do_((function(each){
+return smalltalk.withContext(function($ctx2) {
$2=_st(each)._isClosure();
+if(! smalltalk.assert($2)){
 throw $early=[false];
 };
-}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})});
-_st($2)._do_($3);
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
 return true;
 }
 catch(e) {if(e===$early)return e[0]; throw e}
@@ -1003,23 +997,21 @@ selector: "inlineClosure:",
 fn: function (anIRClosure){
 var self=this;
 var inlinedClosure,statements;
-return smalltalk.withContext(function($ctx1) { 
var $1,$3,$4,$5,$2,$6;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3,$4;
 inlinedClosure=smalltalk.IRSendInliner.fn.prototype._inlineClosure_.apply(_st(self), [anIRClosure]);
 statements=_st(_st(_st(inlinedClosure)._instructions())._last())._instructions();
-$1=statements;
-$2=(function(){
-return smalltalk.withContext(function($ctx2) {
$3=_st(_st(statements)._last())._canBeAssigned();
-if(smalltalk.assert($3)){
-$4=_st((smalltalk.IRAssignment || IRAssignment))._new();
-_st($4)._add_(_st(_st(_st(self)._assignment())._instructions())._first());
-_st($4)._add_(_st(_st(statements)._last())._copy());
-$5=_st($4)._yourself();
-return _st(_st(statements)._last())._replaceWith_($5);
+_st(statements)._ifNotEmpty_((function(){
+return smalltalk.withContext(function($ctx2) {
$1=_st(_st(statements)._last())._canBeAssigned();
+if(smalltalk.assert($1)){
+$2=_st((smalltalk.IRAssignment || IRAssignment))._new();
+_st($2)._add_(_st(_st(_st(self)._assignment())._instructions())._first());
+_st($2)._add_(_st(_st(statements)._last())._copy());
+$3=_st($2)._yourself();
+return _st(_st(statements)._last())._replaceWith_($3);
 };
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-_st($1)._ifNotEmpty_($2);
-$6=inlinedClosure;
-return $6;
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
+$4=inlinedClosure;
+return $4;
 }, function($ctx1) {$ctx1.fill(self,"inlineClosure:",{anIRClosure:anIRClosure,inlinedClosure:inlinedClosure,statements:statements}, smalltalk.IRAssignmentInliner)})}
 }),
 smalltalk.IRAssignmentInliner);
@@ -1063,22 +1055,20 @@ selector: "inlineClosure:",
 fn: function (anIRClosure){
 var self=this;
 var closure,statements;
-return smalltalk.withContext(function($ctx1) { 
var $1,$3,$4,$5,$2,$6;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3,$4;
 closure=smalltalk.IRSendInliner.fn.prototype._inlineClosure_.apply(_st(self), [anIRClosure]);
 statements=_st(_st(_st(closure)._instructions())._last())._instructions();
-$1=statements;
-$2=(function(){
-return smalltalk.withContext(function($ctx2) {
$3=_st(_st(statements)._last())._isReturn();
-if(! smalltalk.assert($3)){
-$4=_st((smalltalk.IRReturn || IRReturn))._new();
-_st($4)._add_(_st(_st(statements)._last())._copy());
-$5=_st($4)._yourself();
-return _st(_st(statements)._last())._replaceWith_($5);
+_st(statements)._ifNotEmpty_((function(){
+return smalltalk.withContext(function($ctx2) {
$1=_st(_st(statements)._last())._isReturn();
+if(! smalltalk.assert($1)){
+$2=_st((smalltalk.IRReturn || IRReturn))._new();
+_st($2)._add_(_st(_st(statements)._last())._copy());
+$3=_st($2)._yourself();
+return _st(_st(statements)._last())._replaceWith_($3);
 };
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-_st($1)._ifNotEmpty_($2);
-$6=closure;
-return $6;
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
+$4=closure;
+return $4;
 }, function($ctx1) {$ctx1.fill(self,"inlineClosure:",{anIRClosure:anIRClosure,closure:closure,statements:statements}, smalltalk.IRReturnInliner)})}
 }),
 smalltalk.IRReturnInliner);

+ 39 - 49
js/Compiler-Inlining.js

@@ -951,37 +951,33 @@ category: 'inlining',
 fn: function (anIRClosure){
 var self=this;
 var inlinedClosure,sequence,statements;
-return smalltalk.withContext(function($ctx1) { 
var $1,$3,$2,$4,$6,$5,$7;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3;
 inlinedClosure=_st(self)._inlinedClosure();
 _st(inlinedClosure)._scope_(_st(anIRClosure)._scope());
-$1=_st(anIRClosure)._instructions();
-$2=(function(each){
-return smalltalk.withContext(function($ctx2) {
$3=_st(each)._isSequence();
-if(! smalltalk.assert($3)){
+_st(_st(anIRClosure)._instructions())._do_((function(each){
+return smalltalk.withContext(function($ctx2) {
$1=_st(each)._isSequence();
+if(! smalltalk.assert($1)){
 return _st(inlinedClosure)._add_(each);
 };
-}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})});
-_st($1)._do_($2);
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
 sequence=_st(self)._inlinedSequence();
 _st(inlinedClosure)._add_(sequence);
 statements=_st(_st(_st(anIRClosure)._instructions())._last())._instructions();
-$4=statements;
-$5=(function(){
+_st(statements)._ifNotEmpty_((function(){
 return smalltalk.withContext(function($ctx2) {
_st(_st(statements)._allButLast())._do_((function(each){
 return smalltalk.withContext(function($ctx3) {
return _st(sequence)._add_(each);
 }, function($ctx3) {$ctx3.fillBlock({each:each},$ctx1)})}));
-$6=_st(_st(_st(statements)._last())._isReturn())._and_((function(){
+$2=_st(_st(_st(statements)._last())._isReturn())._and_((function(){
 return smalltalk.withContext(function($ctx3) {
return _st(_st(statements)._last())._isBlockReturn();
 }, function($ctx3) {$ctx3.fillBlock({},$ctx1)})}));
-if(smalltalk.assert($6)){
+if(smalltalk.assert($2)){
 return _st(sequence)._add_(_st(_st(_st(statements)._last())._instructions())._first());
 } else {
 return _st(sequence)._add_(_st(statements)._last());
 };
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-_st($4)._ifNotEmpty_($5);
-$7=inlinedClosure;
-return $7;
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
+$3=inlinedClosure;
+return $3;
 }, function($ctx1) {$ctx1.fill(self,"inlineClosure:",{anIRClosure:anIRClosure,inlinedClosure:inlinedClosure,sequence:sequence,statements:statements}, smalltalk.IRSendInliner)})},
 args: ["anIRClosure"],
 source: "inlineClosure: anIRClosure\x0a\x09| inlinedClosure sequence statements |\x0a\x0a\x09inlinedClosure := self inlinedClosure.\x0a\x09inlinedClosure scope: anIRClosure scope.\x0a\x0a\x09\x22Add the possible temp declarations\x22\x0a\x09anIRClosure instructions do: [ :each | \x0a\x09\x09each isSequence ifFalse: [\x0a\x09\x09\x09inlinedClosure add: each ]].\x0a\x0a\x09\x22Add a block sequence\x22\x0a\x09sequence := self inlinedSequence.\x0a\x09inlinedClosure add: sequence.\x0a\x0a\x09\x22Get all the statements\x22\x0a\x09statements := anIRClosure instructions last instructions.\x0a\x09\x0a\x09statements ifNotEmpty: [\x0a\x09\x09statements allButLast do: [ :each | sequence add: each ].\x0a\x0a\x09\x09\x22Inlined closures don't have implicit local returns\x22\x0a\x09\x09(statements last isReturn and: [ statements last isBlockReturn ])\x0a\x09\x09\x09ifTrue: [ sequence add: statements last instructions first ]\x0a\x09\x09\x09ifFalse: [ sequence add: statements last ] ].\x0a\x0a\x09^ inlinedClosure",
@@ -1227,21 +1223,19 @@ selector: "shouldInline:",
 category: 'accessing',
 fn: function (anIRInstruction){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$2,$4,$3;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2;
 var $early={};
 try {
 $1=_st(_st(self)._inlinedSelectors())._includes_(_st(anIRInstruction)._selector());
 if(! smalltalk.assert($1)){
 return false;
 };
-$2=_st(_st(anIRInstruction)._instructions())._allButFirst();
-$3=(function(each){
-return smalltalk.withContext(function($ctx2) {
$4=_st(each)._isClosure();
-if(! smalltalk.assert($4)){
+_st(_st(_st(anIRInstruction)._instructions())._allButFirst())._do_((function(each){
+return smalltalk.withContext(function($ctx2) {
$2=_st(each)._isClosure();
+if(! smalltalk.assert($2)){
 throw $early=[false];
 };
-}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})});
-_st($2)._do_($3);
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
 return true;
 }
 catch(e) {if(e===$early)return e[0]; throw e}
@@ -1324,23 +1318,21 @@ category: 'inlining',
 fn: function (anIRClosure){
 var self=this;
 var inlinedClosure,statements;
-return smalltalk.withContext(function($ctx1) { 
var $1,$3,$4,$5,$2,$6;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3,$4;
 inlinedClosure=smalltalk.IRSendInliner.fn.prototype._inlineClosure_.apply(_st(self), [anIRClosure]);
 statements=_st(_st(_st(inlinedClosure)._instructions())._last())._instructions();
-$1=statements;
-$2=(function(){
-return smalltalk.withContext(function($ctx2) {
$3=_st(_st(statements)._last())._canBeAssigned();
-if(smalltalk.assert($3)){
-$4=_st((smalltalk.IRAssignment || IRAssignment))._new();
-_st($4)._add_(_st(_st(_st(self)._assignment())._instructions())._first());
-_st($4)._add_(_st(_st(statements)._last())._copy());
-$5=_st($4)._yourself();
-return _st(_st(statements)._last())._replaceWith_($5);
+_st(statements)._ifNotEmpty_((function(){
+return smalltalk.withContext(function($ctx2) {
$1=_st(_st(statements)._last())._canBeAssigned();
+if(smalltalk.assert($1)){
+$2=_st((smalltalk.IRAssignment || IRAssignment))._new();
+_st($2)._add_(_st(_st(_st(self)._assignment())._instructions())._first());
+_st($2)._add_(_st(_st(statements)._last())._copy());
+$3=_st($2)._yourself();
+return _st(_st(statements)._last())._replaceWith_($3);
 };
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-_st($1)._ifNotEmpty_($2);
-$6=inlinedClosure;
-return $6;
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
+$4=inlinedClosure;
+return $4;
 }, function($ctx1) {$ctx1.fill(self,"inlineClosure:",{anIRClosure:anIRClosure,inlinedClosure:inlinedClosure,statements:statements}, smalltalk.IRAssignmentInliner)})},
 args: ["anIRClosure"],
 source: "inlineClosure: anIRClosure\x0a\x09| inlinedClosure statements |\x0a\x0a\x09inlinedClosure := super inlineClosure: anIRClosure.\x0a\x09statements := inlinedClosure instructions last instructions.\x0a\x09\x0a\x09statements ifNotEmpty: [\x0a\x09\x09statements last canBeAssigned ifTrue: [\x0a\x09\x09\x09statements last replaceWith: (IRAssignment new\x0a\x09\x09\x09\x09add: self assignment instructions first;\x0a\x09\x09\x09\x09add: statements last copy;\x0a\x09\x09\x09\x09yourself) ] ].\x0a\x0a\x09^ inlinedClosure",
@@ -1400,22 +1392,20 @@ category: 'inlining',
 fn: function (anIRClosure){
 var self=this;
 var closure,statements;
-return smalltalk.withContext(function($ctx1) { 
var $1,$3,$4,$5,$2,$6;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3,$4;
 closure=smalltalk.IRSendInliner.fn.prototype._inlineClosure_.apply(_st(self), [anIRClosure]);
 statements=_st(_st(_st(closure)._instructions())._last())._instructions();
-$1=statements;
-$2=(function(){
-return smalltalk.withContext(function($ctx2) {
$3=_st(_st(statements)._last())._isReturn();
-if(! smalltalk.assert($3)){
-$4=_st((smalltalk.IRReturn || IRReturn))._new();
-_st($4)._add_(_st(_st(statements)._last())._copy());
-$5=_st($4)._yourself();
-return _st(_st(statements)._last())._replaceWith_($5);
+_st(statements)._ifNotEmpty_((function(){
+return smalltalk.withContext(function($ctx2) {
$1=_st(_st(statements)._last())._isReturn();
+if(! smalltalk.assert($1)){
+$2=_st((smalltalk.IRReturn || IRReturn))._new();
+_st($2)._add_(_st(_st(statements)._last())._copy());
+$3=_st($2)._yourself();
+return _st(_st(statements)._last())._replaceWith_($3);
 };
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-_st($1)._ifNotEmpty_($2);
-$6=closure;
-return $6;
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
+$4=closure;
+return $4;
 }, function($ctx1) {$ctx1.fill(self,"inlineClosure:",{anIRClosure:anIRClosure,closure:closure,statements:statements}, smalltalk.IRReturnInliner)})},
 args: ["anIRClosure"],
 source: "inlineClosure: anIRClosure\x0a\x09| closure statements |\x0a\x0a\x09closure := super inlineClosure: anIRClosure.\x0a\x09statements := closure instructions last instructions.\x0a\x09\x0a\x09statements ifNotEmpty: [\x0a\x09\x09statements last isReturn\x0a\x09\x09\x09ifFalse: [ statements last replaceWith: (IRReturn new\x0a\x09\x09\x09\x09add: statements last copy;\x0a\x09\x09\x09\x09yourself)] ].\x0a\x0a\x09^ closure",

+ 23 - 24
js/Compiler-Semantic.deploy.js

@@ -173,21 +173,22 @@ selector: "lookupVariable:",
 fn: function (aNode){
 var self=this;
 var lookup;
-return smalltalk.withContext(function($ctx1) { 
var $1,$3,$2,$4;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3;
 lookup=_st(self)._bindingFor_(aNode);
 $1=lookup;
-$2=(function(){
-return smalltalk.withContext(function($ctx2) {
$3=_st(self)._outerScope();
-if(($receiver = $3) == nil || $receiver == undefined){
-lookup=$3;
+if(($receiver = $1) == nil || $receiver == undefined){
+$2=_st(self)._outerScope();
+if(($receiver = $2) == nil || $receiver == undefined){
+lookup=$2;
 } else {
 lookup=_st(_st(self)._outerScope())._lookupVariable_(aNode);
 };
-return lookup;
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-_st($1)._ifNil_($2);
-$4=lookup;
-return $4;
+lookup;
+} else {
+$1;
+};
+$3=lookup;
+return $3;
 }, function($ctx1) {$ctx1.fill(self,"lookupVariable:",{aNode:aNode,lookup:lookup}, smalltalk.LexicalScope)})}
 }),
 smalltalk.LexicalScope);
@@ -919,7 +920,7 @@ var self=this;
 var identifier;
 return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3;
 identifier=_st(aNode)._value();
-$1=_st(_st(_st(["jQuery", "window", "process", "global"])._includes_(identifier))._not())._and_((function(){
+$1=_st(_st(_st(["jQuery", "window", "document", "process", "global"])._includes_(identifier))._not())._and_((function(){
 return smalltalk.withContext(function($ctx2) {
return _st(self)._isVariableGloballyUndefined_(identifier);
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
 if(smalltalk.assert($1)){
@@ -1216,24 +1217,22 @@ smalltalk.method({
 selector: "visitSendNode:",
 fn: function (aNode){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$2,$4,$3;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2;
 $1=_st(_st(_st(aNode)._receiver())._value()).__eq("super");
-$2=(function(){
-return smalltalk.withContext(function($ctx2) {
_st(aNode)._superSend_(true);
+if(smalltalk.assert($1)){
+_st(aNode)._superSend_(true);
 _st(_st(aNode)._receiver())._value_("self");
 _st(_st(self)._superSends())._at_ifAbsentPut_(_st(aNode)._selector(),(function(){
-return smalltalk.withContext(function($ctx3) {
return _st((smalltalk.Set || Set))._new();
-}, function($ctx3) {$ctx3.fillBlock({},$ctx1)})}));
-return _st(_st(_st(self)._superSends())._at_(_st(aNode)._selector()))._add_(aNode);
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-$3=(function(){
-return smalltalk.withContext(function($ctx2) {
$4=_st(_st((smalltalk.IRSendInliner || IRSendInliner))._inlinedSelectors())._includes_(_st(aNode)._selector());
-if(smalltalk.assert($4)){
+return smalltalk.withContext(function($ctx2) {
return _st((smalltalk.Set || Set))._new();
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
+_st(_st(_st(self)._superSends())._at_(_st(aNode)._selector()))._add_(aNode);
+} else {
+$2=_st(_st((smalltalk.IRSendInliner || IRSendInliner))._inlinedSelectors())._includes_(_st(aNode)._selector());
+if(smalltalk.assert($2)){
 _st(aNode)._shouldBeInlined_(true);
-return _st(_st(aNode)._receiver())._shouldBeAliased_(true);
+_st(_st(aNode)._receiver())._shouldBeAliased_(true);
+};
 };
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-_st($1)._ifTrue_ifFalse_($2,$3);
 _st(_st(self)._messageSends())._at_ifAbsentPut_(_st(aNode)._selector(),(function(){
 return smalltalk.withContext(function($ctx2) {
return _st((smalltalk.Set || Set))._new();
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));

+ 24 - 25
js/Compiler-Semantic.js

@@ -235,21 +235,22 @@ category: 'accessing',
 fn: function (aNode){
 var self=this;
 var lookup;
-return smalltalk.withContext(function($ctx1) { 
var $1,$3,$2,$4;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3;
 lookup=_st(self)._bindingFor_(aNode);
 $1=lookup;
-$2=(function(){
-return smalltalk.withContext(function($ctx2) {
$3=_st(self)._outerScope();
-if(($receiver = $3) == nil || $receiver == undefined){
-lookup=$3;
+if(($receiver = $1) == nil || $receiver == undefined){
+$2=_st(self)._outerScope();
+if(($receiver = $2) == nil || $receiver == undefined){
+lookup=$2;
 } else {
 lookup=_st(_st(self)._outerScope())._lookupVariable_(aNode);
 };
-return lookup;
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-_st($1)._ifNil_($2);
-$4=lookup;
-return $4;
+lookup;
+} else {
+$1;
+};
+$3=lookup;
+return $3;
 }, function($ctx1) {$ctx1.fill(self,"lookupVariable:",{aNode:aNode,lookup:lookup}, smalltalk.LexicalScope)})},
 args: ["aNode"],
 source: "lookupVariable: aNode\x0a\x09| lookup |\x0a\x09lookup := (self bindingFor: aNode).\x0a\x09lookup ifNil: [\x0a\x09\x09lookup := self outerScope ifNotNil: [ \x0a\x09\x09\x09(self outerScope lookupVariable: aNode) ]].\x0a\x09^ lookup",
@@ -1246,7 +1247,7 @@ var self=this;
 var identifier;
 return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3;
 identifier=_st(aNode)._value();
-$1=_st(_st(_st(["jQuery", "window", "process", "global"])._includes_(identifier))._not())._and_((function(){
+$1=_st(_st(_st(["jQuery", "window", "document", "process", "global"])._includes_(identifier))._not())._and_((function(){
 return smalltalk.withContext(function($ctx2) {
return _st(self)._isVariableGloballyUndefined_(identifier);
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
 if(smalltalk.assert($1)){
@@ -1259,7 +1260,7 @@ _st(_st(_st(self["@currentScope"])._methodScope())._unknownVariables())._add_(_s
 };
 return self}, function($ctx1) {$ctx1.fill(self,"errorUnknownVariable:",{aNode:aNode,identifier:identifier}, smalltalk.SemanticAnalyzer)})},
 args: ["aNode"],
-source: "errorUnknownVariable: aNode\x0a\x09\x22Throw an error if the variable is undeclared in the global JS scope (i.e. window).\x0a    We allow four variable names in addition: `jQuery`, `window`, `process` and `global` \x0a    for nodejs and browser environments. \x0a    \x0a    This is only to make sure compilation works on both browser-based and nodejs environments.\x0a    The ideal solution would be to use a pragma instead\x22\x0a\x0a\x09| identifier |\x0a    identifier := aNode value.\x0a    \x0a\x09((#('jQuery' 'window' 'process' 'global') includes: identifier) not \x0a        and: [ self isVariableGloballyUndefined: identifier ]) \x0a        \x09ifTrue: [\x0a\x09\x09\x09\x09UnknownVariableError new\x0a\x09\x09\x09\x09\x09variableName: aNode value;\x0a\x09\x09\x09\x09\x09signal ]\x0a\x09\x09\x09ifFalse: [\x0a\x09\x09\x09\x09currentScope methodScope unknownVariables add: aNode value ]",
+source: "errorUnknownVariable: aNode\x0a\x09\x22Throw an error if the variable is undeclared in the global JS scope (i.e. window).\x0a    We allow four variable names in addition: `jQuery`, `window`, `process` and `global` \x0a    for nodejs and browser environments. \x0a    \x0a    This is only to make sure compilation works on both browser-based and nodejs environments.\x0a    The ideal solution would be to use a pragma instead\x22\x0a\x0a\x09| identifier |\x0a    identifier := aNode value.\x0a    \x0a\x09((#('jQuery' 'window' 'document' 'process' 'global') includes: identifier) not \x0a        and: [ self isVariableGloballyUndefined: identifier ]) \x0a        \x09ifTrue: [\x0a\x09\x09\x09\x09UnknownVariableError new\x0a\x09\x09\x09\x09\x09variableName: aNode value;\x0a\x09\x09\x09\x09\x09signal ]\x0a\x09\x09\x09ifFalse: [\x0a\x09\x09\x09\x09currentScope methodScope unknownVariables add: aNode value ]",
 messageSends: ["value", "ifTrue:ifFalse:", "variableName:", "new", "signal", "add:", "unknownVariables", "methodScope", "and:", "isVariableGloballyUndefined:", "not", "includes:"],
 referencedClasses: ["UnknownVariableError"]
 }),
@@ -1633,24 +1634,22 @@ selector: "visitSendNode:",
 category: 'visiting',
 fn: function (aNode){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$2,$4,$3;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2;
 $1=_st(_st(_st(aNode)._receiver())._value()).__eq("super");
-$2=(function(){
-return smalltalk.withContext(function($ctx2) {
_st(aNode)._superSend_(true);
+if(smalltalk.assert($1)){
+_st(aNode)._superSend_(true);
 _st(_st(aNode)._receiver())._value_("self");
 _st(_st(self)._superSends())._at_ifAbsentPut_(_st(aNode)._selector(),(function(){
-return smalltalk.withContext(function($ctx3) {
return _st((smalltalk.Set || Set))._new();
-}, function($ctx3) {$ctx3.fillBlock({},$ctx1)})}));
-return _st(_st(_st(self)._superSends())._at_(_st(aNode)._selector()))._add_(aNode);
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-$3=(function(){
-return smalltalk.withContext(function($ctx2) {
$4=_st(_st((smalltalk.IRSendInliner || IRSendInliner))._inlinedSelectors())._includes_(_st(aNode)._selector());
-if(smalltalk.assert($4)){
+return smalltalk.withContext(function($ctx2) {
return _st((smalltalk.Set || Set))._new();
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
+_st(_st(_st(self)._superSends())._at_(_st(aNode)._selector()))._add_(aNode);
+} else {
+$2=_st(_st((smalltalk.IRSendInliner || IRSendInliner))._inlinedSelectors())._includes_(_st(aNode)._selector());
+if(smalltalk.assert($2)){
 _st(aNode)._shouldBeInlined_(true);
-return _st(_st(aNode)._receiver())._shouldBeAliased_(true);
+_st(_st(aNode)._receiver())._shouldBeAliased_(true);
+};
 };
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-_st($1)._ifTrue_ifFalse_($2,$3);
 _st(_st(self)._messageSends())._at_ifAbsentPut_(_st(aNode)._selector(),(function(){
 return smalltalk.withContext(function($ctx2) {
return _st((smalltalk.Set || Set))._new();
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));

+ 6 - 6
js/Compiler-Tests.js

@@ -407,7 +407,7 @@ _st(_st(self)._interpreter())._step();
 _st(self)._assert_equals_(_st(_st(self)._interpreter())._result(),(3));
 return self}, function($ctx1) {$ctx1.fill(self,"testMessageSend",{}, smalltalk.ASTSteppingInterpreterTest)})},
 args: [],
-source: "testMessageSend\x0a\x09self interpret: 'foo 1 + 2'.\x0a    \x0a    \x22SequenceNode\x22\x0a    self interpreter step.\x0a    \x0a    \x22SendNode\x22\x0a    self interpreter step.\x0a    \x0a     \x22ValueNode\x22\x0a    self interpreter step.\x0a    self assert: self interpreter currentNode value equals: 1.\x0a    \x0a    \x22ValueNode\x22\x0a    self interpreter step.\x0a    self assert: self interpreter currentNode value equals: 2.\x0a    \x0a    \x22Result\x22\x0a    self interpreter step.\x0a    self assert: self interpreter result equals: 3\x0a\x09",
+source: "testMessageSend\x0a\x09self interpret: 'foo 1 + 2'.\x0a    \x0a    \x22SequenceNode\x22\x0a    self interpreter step.\x0a    \x0a    \x22SendNode\x22\x0a    self interpreter step.\x0a    \x0a     \x22ValueNode\x22\x0a    self interpreter step.\x0a    self assert: self interpreter currentNode value equals: 1.\x0a    \x0a    \x22ValueNode\x22\x0a    self interpreter step.\x0a    self assert: self interpreter currentNode value equals: 2.\x0a    \x0a    \x22Result\x22\x0a    self interpreter step.\x0a    self assert: self interpreter result equals: 3",
 messageSends: ["interpret:", "step", "interpreter", "assert:equals:", "value", "currentNode", "result"],
 referencedClasses: []
 }),
@@ -556,7 +556,7 @@ _st(self)._should_return_("foo | a | a := false ifTrue: [ 1 ]. ^ a",nil);
 _st(self)._should_return_("foo | a | ^ a := true ifTrue: [ 1 ]",(1));
 return self}, function($ctx1) {$ctx1.fill(self,"testAssignment",{}, smalltalk.CodeGeneratorTest)})},
 args: [],
-source: "testAssignment\x0a\x09self should: 'foo | a | a := true ifTrue: [ 1 ]. ^ a' return: 1.\x0a\x09self should: 'foo | a | a := false ifTrue: [ 1 ]. ^ a' return: nil.\x0a\x0a\x09self should: 'foo | a | ^ a := true ifTrue: [ 1 ]' return: 1 ",
+source: "testAssignment\x0a\x09self should: 'foo | a | a := true ifTrue: [ 1 ]. ^ a' return: 1.\x0a\x09self should: 'foo | a | a := false ifTrue: [ 1 ]. ^ a' return: nil.\x0a\x0a\x09self should: 'foo | a | ^ a := true ifTrue: [ 1 ]' return: 1",
 messageSends: ["should:return:"],
 referencedClasses: []
 }),
@@ -606,7 +606,7 @@ var self=this;
 return smalltalk.withContext(function($ctx1) { 
_st(self)._should_return_("foo\x0a  | x |\x0a  x := 1.\x0a  ^ { x. true ifTrue: [ x := 2 ] }\x0a",[(1), (2)]);
 return self}, function($ctx1) {$ctx1.fill(self,"testDynamicArrayElementsOrdered",{}, smalltalk.CodeGeneratorTest)})},
 args: [],
-source: "testDynamicArrayElementsOrdered\x0a\x09self should: 'foo\x0a  | x |\x0a  x := 1.\x0a  ^ { x. true ifTrue: [ x := 2 ] }\x0a' return: #(1 2).\x0a",
+source: "testDynamicArrayElementsOrdered\x0a\x09self should: 'foo\x0a  | x |\x0a  x := 1.\x0a  ^ { x. true ifTrue: [ x := 2 ] }\x0a' return: #(1 2).",
 messageSends: ["should:return:"],
 referencedClasses: []
 }),
@@ -622,7 +622,7 @@ var self=this;
 return smalltalk.withContext(function($ctx1) { 
_st(self)._should_return_("foo\x0a  | x |\x0a  x := 'foo'->1.\x0a  ^ #{ x. (true ifTrue: [ x := 'bar'->2 ]) }\x0a",smalltalk.HashedCollection._fromPairs_([_st("foo").__minus_gt((1)),_st("bar").__minus_gt((2))]));
 return self}, function($ctx1) {$ctx1.fill(self,"testDynamicDictionaryElementsOrdered",{}, smalltalk.CodeGeneratorTest)})},
 args: [],
-source: "testDynamicDictionaryElementsOrdered\x0a\x09self should: 'foo\x0a  | x |\x0a  x := ''foo''->1.\x0a  ^ #{ x. (true ifTrue: [ x := ''bar''->2 ]) }\x0a' return: #{'foo'->1. 'bar'->2}.\x0a",
+source: "testDynamicDictionaryElementsOrdered\x0a\x09self should: 'foo\x0a  | x |\x0a  x := ''foo''->1.\x0a  ^ #{ x. (true ifTrue: [ x := ''bar''->2 ]) }\x0a' return: #{'foo'->1. 'bar'->2}.",
 messageSends: ["should:return:", "->"],
 referencedClasses: []
 }),
@@ -641,7 +641,7 @@ _st(self)._should_return_("foo\x0a  | x |\x0a  x := 1.\x0a  ^ { 'foo'->x. 'bar'-
 _st(self)._should_return_("foo\x0a  | x |\x0a  x := 1.\x0a  ^ #{ 'foo'->x. 'bar'->(true ifTrue: [ x := 2 ]) }\x0a",smalltalk.HashedCollection._fromPairs_([_st("foo").__minus_gt((1)),_st("bar").__minus_gt((2))]));
 return self}, function($ctx1) {$ctx1.fill(self,"testInnerTemporalDependentElementsOrdered",{}, smalltalk.CodeGeneratorTest)})},
 args: [],
-source: "testInnerTemporalDependentElementsOrdered\x0a\x09self should: 'foo\x0a  | x |\x0a  x := Array.\x0a  ^ x with: ''foo''->x with: ''bar''->(true ifTrue: [ x := 2 ])\x0a' return: {'foo'->Array. 'bar'->2}.\x0a\x09self should: 'foo\x0a  | x |\x0a  x := 1.\x0a  ^ Array with: ''foo''->x with: ''bar''->(true ifTrue: [ x := 2 ])\x0a' return: {'foo'->1. 'bar'->2}.\x0a\x09self should: 'foo\x0a  | x |\x0a  x := 1.\x0a  ^ { ''foo''->x. ''bar''->(true ifTrue: [ x := 2 ]) }\x0a' return: {'foo'->1. 'bar'->2}.\x0a\x09self should: 'foo\x0a  | x |\x0a  x := 1.\x0a  ^ #{ ''foo''->x. ''bar''->(true ifTrue: [ x := 2 ]) }\x0a' return: #{'foo'->1. 'bar'->2}.\x0a",
+source: "testInnerTemporalDependentElementsOrdered\x0a\x09self should: 'foo\x0a  | x |\x0a  x := Array.\x0a  ^ x with: ''foo''->x with: ''bar''->(true ifTrue: [ x := 2 ])\x0a' return: {'foo'->Array. 'bar'->2}.\x0a\x09self should: 'foo\x0a  | x |\x0a  x := 1.\x0a  ^ Array with: ''foo''->x with: ''bar''->(true ifTrue: [ x := 2 ])\x0a' return: {'foo'->1. 'bar'->2}.\x0a\x09self should: 'foo\x0a  | x |\x0a  x := 1.\x0a  ^ { ''foo''->x. ''bar''->(true ifTrue: [ x := 2 ]) }\x0a' return: {'foo'->1. 'bar'->2}.\x0a\x09self should: 'foo\x0a  | x |\x0a  x := 1.\x0a  ^ #{ ''foo''->x. ''bar''->(true ifTrue: [ x := 2 ]) }\x0a' return: #{'foo'->1. 'bar'->2}.",
 messageSends: ["should:return:", "->"],
 referencedClasses: ["Array"]
 }),
@@ -760,7 +760,7 @@ return smalltalk.withContext(function($ctx1) { 
_st(self)._should_return_("foo\x
 _st(self)._should_return_("foo\x0a  | x |\x0a  x := Array.\x0a  ^ x with: x with: (true ifTrue: [ x := 2 ])\x0a",[(smalltalk.Array || Array),(2)]);
 return self}, function($ctx1) {$ctx1.fill(self,"testSendReceiverAndArgumentsOrdered",{}, smalltalk.CodeGeneratorTest)})},
 args: [],
-source: "testSendReceiverAndArgumentsOrdered\x0a\x09self should: 'foo\x0a  | x |\x0a  x := 1.\x0a  ^ Array with: x with: (true ifTrue: [ x := 2 ])\x0a' return: #(1 2).\x0a\x0a\x09self should: 'foo\x0a  | x |\x0a  x := Array.\x0a  ^ x with: x with: (true ifTrue: [ x := 2 ])\x0a' return: {Array. 2}.\x0a",
+source: "testSendReceiverAndArgumentsOrdered\x0a\x09self should: 'foo\x0a  | x |\x0a  x := 1.\x0a  ^ Array with: x with: (true ifTrue: [ x := 2 ])\x0a' return: #(1 2).\x0a\x0a\x09self should: 'foo\x0a  | x |\x0a  x := Array.\x0a  ^ x with: x with: (true ifTrue: [ x := 2 ])\x0a' return: {Array. 2}.",
 messageSends: ["should:return:"],
 referencedClasses: ["Array"]
 }),

文件差异内容过多而无法显示
+ 270 - 104
js/Documentation.deploy.js


文件差异内容过多而无法显示
+ 288 - 122
js/Documentation.js


文件差异内容过多而无法显示
+ 320 - 376
js/IDE.deploy.js


文件差异内容过多而无法显示
+ 244 - 290
js/IDE.js


+ 78 - 112
js/Importer-Exporter.deploy.js

@@ -7,30 +7,27 @@ selector: "nextChunk",
 fn: function (){
 var self=this;
 var char,result,chunk;
-return smalltalk.withContext(function($ctx1) { 
var $1,$3,$5,$6,$4,$2;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3;
 var $early={};
 try {
 result=_st("")._writeStream();
-$1=(function(){
+_st((function(){
 return smalltalk.withContext(function($ctx2) {
char=_st(self["@stream"])._next();
 char;
 return _st(char)._notNil();
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-$2=(function(){
-return smalltalk.withContext(function($ctx2) {
$3=_st(char).__eq("!");
-$4=(function(){
-return smalltalk.withContext(function($ctx3) {
$5=_st(_st(self["@stream"])._peek()).__eq("!");
-if(smalltalk.assert($5)){
-return _st(self["@stream"])._next();
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}))._whileTrue_((function(){
+return smalltalk.withContext(function($ctx2) {
$1=_st(char).__eq("!");
+if(smalltalk.assert($1)){
+$2=_st(_st(self["@stream"])._peek()).__eq("!");
+if(smalltalk.assert($2)){
+_st(self["@stream"])._next();
 } else {
-$6=_st(_st(result)._contents())._trimBoth();
-throw $early=[$6];
+$3=_st(_st(result)._contents())._trimBoth();
+throw $early=[$3];
+};
 };
-}, function($ctx3) {$ctx3.fillBlock({},$ctx1)})});
-_st($3)._ifTrue_($4);
 return _st(result)._nextPut_(char);
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-_st($1)._whileTrue_($2);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
 return nil;
 }
 catch(e) {if(e===$early)return e[0]; throw e}
@@ -71,20 +68,18 @@ smalltalk.method({
 selector: "classNameFor:",
 fn: function (aClass){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $2,$3,$5,$4,$1;
+return smalltalk.withContext(function($ctx1) { 
var $2,$3,$1;
 $2=_st(aClass)._isMetaclass();
-$3=(function(){
-return smalltalk.withContext(function($ctx2) {
return _st(_st(_st(aClass)._instanceClass())._name()).__comma(".klass");
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-$4=(function(){
-return smalltalk.withContext(function($ctx2) {
$5=_st(aClass)._isNil();
-if(smalltalk.assert($5)){
-return "nil";
+if(smalltalk.assert($2)){
+$1=_st(_st(_st(aClass)._instanceClass())._name()).__comma(".klass");
+} else {
+$3=_st(aClass)._isNil();
+if(smalltalk.assert($3)){
+$1="nil";
 } else {
-return _st(aClass)._name();
+$1=_st(aClass)._name();
+};
 };
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-$1=_st($2)._ifTrue_ifFalse_($3,$4);
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"classNameFor:",{aClass:aClass}, smalltalk.Exporter)})}
 }),
@@ -230,17 +225,15 @@ smalltalk.method({
 selector: "exportMethodsOf:on:",
 fn: function (aClass,aStream){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$3,$2;
-$1=_st(_st(_st(aClass)._methodDictionary())._values())._sorted_((function(a,b){
+return smalltalk.withContext(function($ctx1) { 
var $1;
+_st(_st(_st(_st(aClass)._methodDictionary())._values())._sorted_((function(a,b){
 return smalltalk.withContext(function($ctx2) {
return _st(_st(a)._selector()).__lt_eq(_st(b)._selector());
-}, function($ctx2) {$ctx2.fillBlock({a:a,b:b},$ctx1)})}));
-$2=(function(each){
-return smalltalk.withContext(function($ctx2) {
$3=_st(_st(each)._category())._match_("^\x5c*");
-if(! smalltalk.assert($3)){
+}, function($ctx2) {$ctx2.fillBlock({a:a,b:b},$ctx1)})})))._do_((function(each){
+return smalltalk.withContext(function($ctx2) {
$1=_st(_st(each)._category())._match_("^\x5c*");
+if(! smalltalk.assert($1)){
 return _st(self)._exportMethod_of_on_(each,aClass,aStream);
 };
-}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})});
-_st($1)._do_($2);
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
 _st(aStream)._lf();
 return self}, function($ctx1) {$ctx1.fill(self,"exportMethodsOf:on:",{aClass:aClass,aStream:aStream}, smalltalk.Exporter)})}
 }),
@@ -290,26 +283,20 @@ selector: "exportPackageExtensionsOf:on:",
 fn: function (package_,aStream){
 var self=this;
 var name;
-return smalltalk.withContext(function($ctx1) { 
var $1,$3,$5,$7,$6,$4,$2;
+return smalltalk.withContext(function($ctx1) { 
var $1;
 name=_st(package_)._name();
-$1=_st((smalltalk.Package || Package))._sortedClasses_(_st(_st((smalltalk.Smalltalk || Smalltalk))._current())._classes());
-$2=(function(each){
-return smalltalk.withContext(function($ctx2) {
$3=[each,_st(each)._class()];
-$4=(function(aClass){
-return smalltalk.withContext(function($ctx3) {
$5=_st(_st(_st(aClass)._methodDictionary())._values())._sorted_((function(a,b){
+_st(_st((smalltalk.Package || Package))._sortedClasses_(_st(_st((smalltalk.Smalltalk || Smalltalk))._current())._classes()))._do_((function(each){
+return smalltalk.withContext(function($ctx2) {
return _st([each,_st(each)._class()])._do_((function(aClass){
+return smalltalk.withContext(function($ctx3) {
return _st(_st(_st(_st(aClass)._methodDictionary())._values())._sorted_((function(a,b){
 return smalltalk.withContext(function($ctx4) {
return _st(_st(a)._selector()).__lt_eq(_st(b)._selector());
-}, function($ctx4) {$ctx4.fillBlock({a:a,b:b},$ctx1)})}));
-$6=(function(method){
-return smalltalk.withContext(function($ctx4) {
$7=_st(_st(method)._category())._match_(_st("^\x5c*").__comma(name));
-if(smalltalk.assert($7)){
+}, function($ctx4) {$ctx4.fillBlock({a:a,b:b},$ctx1)})})))._do_((function(method){
+return smalltalk.withContext(function($ctx4) {
$1=_st(_st(method)._category())._match_(_st("^\x5c*").__comma(name));
+if(smalltalk.assert($1)){
 return _st(self)._exportMethod_of_on_(method,aClass,aStream);
 };
-}, function($ctx4) {$ctx4.fillBlock({method:method},$ctx1)})});
-return _st($5)._do_($6);
-}, function($ctx3) {$ctx3.fillBlock({aClass:aClass},$ctx1)})});
-return _st($3)._do_($4);
-}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})});
-_st($1)._do_($2);
+}, function($ctx4) {$ctx4.fillBlock({method:method},$ctx1)})}));
+}, function($ctx3) {$ctx3.fillBlock({aClass:aClass},$ctx1)})}));
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
 return self}, function($ctx1) {$ctx1.fill(self,"exportPackageExtensionsOf:on:",{package_:package_,aStream:aStream,name:name}, smalltalk.Exporter)})}
 }),
 smalltalk.Exporter);
@@ -336,20 +323,18 @@ smalltalk.method({
 selector: "classNameFor:",
 fn: function (aClass){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $2,$3,$5,$4,$1;
+return smalltalk.withContext(function($ctx1) { 
var $2,$3,$1;
 $2=_st(aClass)._isMetaclass();
-$3=(function(){
-return smalltalk.withContext(function($ctx2) {
return _st(_st(_st(aClass)._instanceClass())._name()).__comma(" class");
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-$4=(function(){
-return smalltalk.withContext(function($ctx2) {
$5=_st(aClass)._isNil();
-if(smalltalk.assert($5)){
-return "nil";
+if(smalltalk.assert($2)){
+$1=_st(_st(_st(aClass)._instanceClass())._name()).__comma(" class");
 } else {
-return _st(aClass)._name();
+$3=_st(aClass)._isNil();
+if(smalltalk.assert($3)){
+$1="nil";
+} else {
+$1=_st(aClass)._name();
+};
 };
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-$1=_st($2)._ifTrue_ifFalse_($3,$4);
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"classNameFor:",{aClass:aClass}, smalltalk.ChunkExporter)})}
 }),
@@ -466,16 +451,14 @@ selector: "exportMethodsOf:on:",
 fn: function (aClass,aStream){
 var self=this;
 var map;
-return smalltalk.withContext(function($ctx1) { 
var $1,$3,$2;
+return smalltalk.withContext(function($ctx1) { 
var $1;
 map=_st((smalltalk.Dictionary || Dictionary))._new();
-$1=aClass;
-$2=(function(category,methods){
-return smalltalk.withContext(function($ctx2) {
$3=_st(category)._match_("^\x5c*");
-if(! smalltalk.assert($3)){
+_st(aClass)._protocolsDo_((function(category,methods){
+return smalltalk.withContext(function($ctx2) {
$1=_st(category)._match_("^\x5c*");
+if(! smalltalk.assert($1)){
 return _st(map)._at_put_(category,methods);
 };
-}, function($ctx2) {$ctx2.fillBlock({category:category,methods:methods},$ctx1)})});
-_st($1)._protocolsDo_($2);
+}, function($ctx2) {$ctx2.fillBlock({category:category,methods:methods},$ctx1)})}));
 _st(_st(_st(map)._keys())._sorted_((function(a,b){
 return smalltalk.withContext(function($ctx2) {
return _st(a).__lt_eq(b);
 }, function($ctx2) {$ctx2.fillBlock({a:a,b:b},$ctx1)})})))._do_((function(category){
@@ -509,22 +492,18 @@ selector: "exportPackageExtensionsOf:on:",
 fn: function (package_,aStream){
 var self=this;
 var name,map;
-return smalltalk.withContext(function($ctx1) { 
var $1,$3,$5,$7,$6,$4,$2;
+return smalltalk.withContext(function($ctx1) { 
var $1;
 name=_st(package_)._name();
-$1=_st((smalltalk.Package || Package))._sortedClasses_(_st(_st((smalltalk.Smalltalk || Smalltalk))._current())._classes());
-$2=(function(each){
-return smalltalk.withContext(function($ctx2) {
$3=[each,_st(each)._class()];
-$4=(function(aClass){
+_st(_st((smalltalk.Package || Package))._sortedClasses_(_st(_st((smalltalk.Smalltalk || Smalltalk))._current())._classes()))._do_((function(each){
+return smalltalk.withContext(function($ctx2) {
return _st([each,_st(each)._class()])._do_((function(aClass){
 return smalltalk.withContext(function($ctx3) {
map=_st((smalltalk.Dictionary || Dictionary))._new();
 map;
-$5=aClass;
-$6=(function(category,methods){
-return smalltalk.withContext(function($ctx4) {
$7=_st(category)._match_(_st("^\x5c*").__comma(name));
-if(smalltalk.assert($7)){
+_st(aClass)._protocolsDo_((function(category,methods){
+return smalltalk.withContext(function($ctx4) {
$1=_st(category)._match_(_st("^\x5c*").__comma(name));
+if(smalltalk.assert($1)){
 return _st(map)._at_put_(category,methods);
 };
-}, function($ctx4) {$ctx4.fillBlock({category:category,methods:methods},$ctx1)})});
-_st($5)._protocolsDo_($6);
+}, function($ctx4) {$ctx4.fillBlock({category:category,methods:methods},$ctx1)})}));
 return _st(_st(_st(map)._keys())._sorted_((function(a,b){
 return smalltalk.withContext(function($ctx4) {
return _st(a).__lt_eq(b);
 }, function($ctx4) {$ctx4.fillBlock({a:a,b:b},$ctx1)})})))._do_((function(category){
@@ -533,10 +512,8 @@ return smalltalk.withContext(function($ctx4) {
methods=_st(map)._at_(category);
 methods;
 return _st(self)._exportMethods_category_of_on_(methods,category,aClass,aStream);
 }, function($ctx4) {$ctx4.fillBlock({category:category,methods:methods},$ctx1)})}));
-}, function($ctx3) {$ctx3.fillBlock({aClass:aClass},$ctx1)})});
-return _st($3)._do_($4);
-}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})});
-_st($1)._do_($2);
+}, function($ctx3) {$ctx3.fillBlock({aClass:aClass},$ctx1)})}));
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
 return self}, function($ctx1) {$ctx1.fill(self,"exportPackageExtensionsOf:on:",{package_:package_,aStream:aStream,name:name,map:map}, smalltalk.ChunkExporter)})}
 }),
 smalltalk.ChunkExporter);
@@ -608,33 +585,29 @@ selector: "import:",
 fn: function (aStream){
 var self=this;
 var chunk,result,parser,lastEmpty;
-return smalltalk.withContext(function($ctx1) { 
var $1,$3,$4,$6,$5,$2;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2;
 parser=_st((smalltalk.ChunkParser || ChunkParser))._on_(aStream);
 lastEmpty=false;
-$1=(function(){
+_st((function(){
 return smalltalk.withContext(function($ctx2) {
chunk=_st(parser)._nextChunk();
 chunk;
 return _st(chunk)._isNil();
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-$2=(function(){
-return smalltalk.withContext(function($ctx2) {
$3=_st(chunk)._isEmpty();
-$4=(function(){
-return smalltalk.withContext(function($ctx3) {
lastEmpty=true;
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}))._whileFalse_((function(){
+return smalltalk.withContext(function($ctx2) {
$1=_st(chunk)._isEmpty();
+if(smalltalk.assert($1)){
+lastEmpty=true;
 return lastEmpty;
-}, function($ctx3) {$ctx3.fillBlock({},$ctx1)})});
-$5=(function(){
-return smalltalk.withContext(function($ctx3) {
result=_st(_st((smalltalk.Compiler || Compiler))._new())._evaluateExpression_(chunk);
+} else {
+result=_st(_st((smalltalk.Compiler || Compiler))._new())._evaluateExpression_(chunk);
 result;
-$6=lastEmpty;
-if(smalltalk.assert($6)){
+$2=lastEmpty;
+if(smalltalk.assert($2)){
 lastEmpty=false;
 lastEmpty;
 return _st(result)._scanFrom_(parser);
 };
-}, function($ctx3) {$ctx3.fillBlock({},$ctx1)})});
-return _st($3)._ifTrue_ifFalse_($4,$5);
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-_st($1)._whileFalse_($2);
+};
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
 return self}, function($ctx1) {$ctx1.fill(self,"import:",{aStream:aStream,chunk:chunk,result:result,parser:parser,lastEmpty:lastEmpty}, smalltalk.Importer)})}
 }),
 smalltalk.Importer);
@@ -664,23 +637,16 @@ selector: "loadPackage:prefix:",
 fn: function (packageName,aString){
 var self=this;
 var url;
-return smalltalk.withContext(function($ctx1) { 
var $1,$2,$4,$5,$8,$7,$6,$3;
+return smalltalk.withContext(function($ctx1) { 
var $1;
 url=_st(_st(_st(_st("/").__comma(aString)).__comma("/js/")).__comma(packageName)).__comma(".js");
-$1=jQuery;
-$2=url;
-$4=_st("type").__minus_gt("GET");
-$5=_st("dataType").__minus_gt("script");
-$7=(function(jqXHR,textStatus){
-return smalltalk.withContext(function($ctx2) {
$8=_st(_st(jqXHR)._readyState()).__eq((4));
-if(smalltalk.assert($8)){
+_st(jQuery)._ajax_options_(url,smalltalk.HashedCollection._fromPairs_([_st("type").__minus_gt("GET"),_st("dataType").__minus_gt("script"),_st("complete").__minus_gt((function(jqXHR,textStatus){
+return smalltalk.withContext(function($ctx2) {
$1=_st(_st(jqXHR)._readyState()).__eq((4));
+if(smalltalk.assert($1)){
 return _st(self)._initializePackageNamed_prefix_(packageName,aString);
 };
-}, function($ctx2) {$ctx2.fillBlock({jqXHR:jqXHR,textStatus:textStatus},$ctx1)})});
-$6=_st("complete").__minus_gt($7);
-$3=smalltalk.HashedCollection._fromPairs_([$4,$5,$6,_st("error").__minus_gt((function(){
+}, function($ctx2) {$ctx2.fillBlock({jqXHR:jqXHR,textStatus:textStatus},$ctx1)})})),_st("error").__minus_gt((function(){
 return smalltalk.withContext(function($ctx2) {
return _st(window)._alert_(_st("Could not load package at:  ").__comma(url));
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}))]);
-_st($1)._ajax_options_($2,$3);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}))]));
 return self}, function($ctx1) {$ctx1.fill(self,"loadPackage:prefix:",{packageName:packageName,aString:aString,url:url}, smalltalk.PackageLoader)})}
 }),
 smalltalk.PackageLoader);

+ 78 - 112
js/Importer-Exporter.js

@@ -8,30 +8,27 @@ category: 'reading',
 fn: function (){
 var self=this;
 var char,result,chunk;
-return smalltalk.withContext(function($ctx1) { 
var $1,$3,$5,$6,$4,$2;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3;
 var $early={};
 try {
 result=_st("")._writeStream();
-$1=(function(){
+_st((function(){
 return smalltalk.withContext(function($ctx2) {
char=_st(self["@stream"])._next();
 char;
 return _st(char)._notNil();
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-$2=(function(){
-return smalltalk.withContext(function($ctx2) {
$3=_st(char).__eq("!");
-$4=(function(){
-return smalltalk.withContext(function($ctx3) {
$5=_st(_st(self["@stream"])._peek()).__eq("!");
-if(smalltalk.assert($5)){
-return _st(self["@stream"])._next();
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}))._whileTrue_((function(){
+return smalltalk.withContext(function($ctx2) {
$1=_st(char).__eq("!");
+if(smalltalk.assert($1)){
+$2=_st(_st(self["@stream"])._peek()).__eq("!");
+if(smalltalk.assert($2)){
+_st(self["@stream"])._next();
 } else {
-$6=_st(_st(result)._contents())._trimBoth();
-throw $early=[$6];
+$3=_st(_st(result)._contents())._trimBoth();
+throw $early=[$3];
+};
 };
-}, function($ctx3) {$ctx3.fillBlock({},$ctx1)})});
-_st($3)._ifTrue_($4);
 return _st(result)._nextPut_(char);
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-_st($1)._whileTrue_($2);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
 return nil;
 }
 catch(e) {if(e===$early)return e[0]; throw e}
@@ -87,20 +84,18 @@ selector: "classNameFor:",
 category: 'private',
 fn: function (aClass){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $2,$3,$5,$4,$1;
+return smalltalk.withContext(function($ctx1) { 
var $2,$3,$1;
 $2=_st(aClass)._isMetaclass();
-$3=(function(){
-return smalltalk.withContext(function($ctx2) {
return _st(_st(_st(aClass)._instanceClass())._name()).__comma(".klass");
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-$4=(function(){
-return smalltalk.withContext(function($ctx2) {
$5=_st(aClass)._isNil();
-if(smalltalk.assert($5)){
-return "nil";
+if(smalltalk.assert($2)){
+$1=_st(_st(_st(aClass)._instanceClass())._name()).__comma(".klass");
+} else {
+$3=_st(aClass)._isNil();
+if(smalltalk.assert($3)){
+$1="nil";
 } else {
-return _st(aClass)._name();
+$1=_st(aClass)._name();
+};
 };
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-$1=_st($2)._ifTrue_ifFalse_($3,$4);
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"classNameFor:",{aClass:aClass}, smalltalk.Exporter)})},
 args: ["aClass"],
@@ -276,17 +271,15 @@ selector: "exportMethodsOf:on:",
 category: 'private',
 fn: function (aClass,aStream){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$3,$2;
-$1=_st(_st(_st(aClass)._methodDictionary())._values())._sorted_((function(a,b){
+return smalltalk.withContext(function($ctx1) { 
var $1;
+_st(_st(_st(_st(aClass)._methodDictionary())._values())._sorted_((function(a,b){
 return smalltalk.withContext(function($ctx2) {
return _st(_st(a)._selector()).__lt_eq(_st(b)._selector());
-}, function($ctx2) {$ctx2.fillBlock({a:a,b:b},$ctx1)})}));
-$2=(function(each){
-return smalltalk.withContext(function($ctx2) {
$3=_st(_st(each)._category())._match_("^\x5c*");
-if(! smalltalk.assert($3)){
+}, function($ctx2) {$ctx2.fillBlock({a:a,b:b},$ctx1)})})))._do_((function(each){
+return smalltalk.withContext(function($ctx2) {
$1=_st(_st(each)._category())._match_("^\x5c*");
+if(! smalltalk.assert($1)){
 return _st(self)._exportMethod_of_on_(each,aClass,aStream);
 };
-}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})});
-_st($1)._do_($2);
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
 _st(aStream)._lf();
 return self}, function($ctx1) {$ctx1.fill(self,"exportMethodsOf:on:",{aClass:aClass,aStream:aStream}, smalltalk.Exporter)})},
 args: ["aClass", "aStream"],
@@ -351,26 +344,20 @@ category: 'private',
 fn: function (package_,aStream){
 var self=this;
 var name;
-return smalltalk.withContext(function($ctx1) { 
var $1,$3,$5,$7,$6,$4,$2;
+return smalltalk.withContext(function($ctx1) { 
var $1;
 name=_st(package_)._name();
-$1=_st((smalltalk.Package || Package))._sortedClasses_(_st(_st((smalltalk.Smalltalk || Smalltalk))._current())._classes());
-$2=(function(each){
-return smalltalk.withContext(function($ctx2) {
$3=[each,_st(each)._class()];
-$4=(function(aClass){
-return smalltalk.withContext(function($ctx3) {
$5=_st(_st(_st(aClass)._methodDictionary())._values())._sorted_((function(a,b){
+_st(_st((smalltalk.Package || Package))._sortedClasses_(_st(_st((smalltalk.Smalltalk || Smalltalk))._current())._classes()))._do_((function(each){
+return smalltalk.withContext(function($ctx2) {
return _st([each,_st(each)._class()])._do_((function(aClass){
+return smalltalk.withContext(function($ctx3) {
return _st(_st(_st(_st(aClass)._methodDictionary())._values())._sorted_((function(a,b){
 return smalltalk.withContext(function($ctx4) {
return _st(_st(a)._selector()).__lt_eq(_st(b)._selector());
-}, function($ctx4) {$ctx4.fillBlock({a:a,b:b},$ctx1)})}));
-$6=(function(method){
-return smalltalk.withContext(function($ctx4) {
$7=_st(_st(method)._category())._match_(_st("^\x5c*").__comma(name));
-if(smalltalk.assert($7)){
+}, function($ctx4) {$ctx4.fillBlock({a:a,b:b},$ctx1)})})))._do_((function(method){
+return smalltalk.withContext(function($ctx4) {
$1=_st(_st(method)._category())._match_(_st("^\x5c*").__comma(name));
+if(smalltalk.assert($1)){
 return _st(self)._exportMethod_of_on_(method,aClass,aStream);
 };
-}, function($ctx4) {$ctx4.fillBlock({method:method},$ctx1)})});
-return _st($5)._do_($6);
-}, function($ctx3) {$ctx3.fillBlock({aClass:aClass},$ctx1)})});
-return _st($3)._do_($4);
-}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})});
-_st($1)._do_($2);
+}, function($ctx4) {$ctx4.fillBlock({method:method},$ctx1)})}));
+}, function($ctx3) {$ctx3.fillBlock({aClass:aClass},$ctx1)})}));
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
 return self}, function($ctx1) {$ctx1.fill(self,"exportPackageExtensionsOf:on:",{package_:package_,aStream:aStream,name:name}, smalltalk.Exporter)})},
 args: ["package", "aStream"],
 source: "exportPackageExtensionsOf: package on: aStream\x0a\x09\x22Issue #143: sort classes and methods alphabetically\x22\x0a\x0a\x09| name |\x0a\x09name := package name.\x0a\x09(Package sortedClasses: Smalltalk current classes) do: [:each |\x0a\x09\x09{each. each class} do: [:aClass | \x0a\x09\x09\x09((aClass methodDictionary values) sorted: [:a :b | a selector <= b selector]) do: [:method |\x0a\x09\x09\x09\x09(method category match: '^\x5c*', name) ifTrue: [\x0a\x09\x09\x09\x09\x09self exportMethod: method of: aClass on: aStream ]]]]",
@@ -407,20 +394,18 @@ selector: "classNameFor:",
 category: 'not yet classified',
 fn: function (aClass){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $2,$3,$5,$4,$1;
+return smalltalk.withContext(function($ctx1) { 
var $2,$3,$1;
 $2=_st(aClass)._isMetaclass();
-$3=(function(){
-return smalltalk.withContext(function($ctx2) {
return _st(_st(_st(aClass)._instanceClass())._name()).__comma(" class");
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-$4=(function(){
-return smalltalk.withContext(function($ctx2) {
$5=_st(aClass)._isNil();
-if(smalltalk.assert($5)){
-return "nil";
+if(smalltalk.assert($2)){
+$1=_st(_st(_st(aClass)._instanceClass())._name()).__comma(" class");
 } else {
-return _st(aClass)._name();
+$3=_st(aClass)._isNil();
+if(smalltalk.assert($3)){
+$1="nil";
+} else {
+$1=_st(aClass)._name();
+};
 };
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-$1=_st($2)._ifTrue_ifFalse_($3,$4);
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"classNameFor:",{aClass:aClass}, smalltalk.ChunkExporter)})},
 args: ["aClass"],
@@ -562,16 +547,14 @@ category: 'not yet classified',
 fn: function (aClass,aStream){
 var self=this;
 var map;
-return smalltalk.withContext(function($ctx1) { 
var $1,$3,$2;
+return smalltalk.withContext(function($ctx1) { 
var $1;
 map=_st((smalltalk.Dictionary || Dictionary))._new();
-$1=aClass;
-$2=(function(category,methods){
-return smalltalk.withContext(function($ctx2) {
$3=_st(category)._match_("^\x5c*");
-if(! smalltalk.assert($3)){
+_st(aClass)._protocolsDo_((function(category,methods){
+return smalltalk.withContext(function($ctx2) {
$1=_st(category)._match_("^\x5c*");
+if(! smalltalk.assert($1)){
 return _st(map)._at_put_(category,methods);
 };
-}, function($ctx2) {$ctx2.fillBlock({category:category,methods:methods},$ctx1)})});
-_st($1)._protocolsDo_($2);
+}, function($ctx2) {$ctx2.fillBlock({category:category,methods:methods},$ctx1)})}));
 _st(_st(_st(map)._keys())._sorted_((function(a,b){
 return smalltalk.withContext(function($ctx2) {
return _st(a).__lt_eq(b);
 }, function($ctx2) {$ctx2.fillBlock({a:a,b:b},$ctx1)})})))._do_((function(category){
@@ -615,22 +598,18 @@ category: 'not yet classified',
 fn: function (package_,aStream){
 var self=this;
 var name,map;
-return smalltalk.withContext(function($ctx1) { 
var $1,$3,$5,$7,$6,$4,$2;
+return smalltalk.withContext(function($ctx1) { 
var $1;
 name=_st(package_)._name();
-$1=_st((smalltalk.Package || Package))._sortedClasses_(_st(_st((smalltalk.Smalltalk || Smalltalk))._current())._classes());
-$2=(function(each){
-return smalltalk.withContext(function($ctx2) {
$3=[each,_st(each)._class()];
-$4=(function(aClass){
+_st(_st((smalltalk.Package || Package))._sortedClasses_(_st(_st((smalltalk.Smalltalk || Smalltalk))._current())._classes()))._do_((function(each){
+return smalltalk.withContext(function($ctx2) {
return _st([each,_st(each)._class()])._do_((function(aClass){
 return smalltalk.withContext(function($ctx3) {
map=_st((smalltalk.Dictionary || Dictionary))._new();
 map;
-$5=aClass;
-$6=(function(category,methods){
-return smalltalk.withContext(function($ctx4) {
$7=_st(category)._match_(_st("^\x5c*").__comma(name));
-if(smalltalk.assert($7)){
+_st(aClass)._protocolsDo_((function(category,methods){
+return smalltalk.withContext(function($ctx4) {
$1=_st(category)._match_(_st("^\x5c*").__comma(name));
+if(smalltalk.assert($1)){
 return _st(map)._at_put_(category,methods);
 };
-}, function($ctx4) {$ctx4.fillBlock({category:category,methods:methods},$ctx1)})});
-_st($5)._protocolsDo_($6);
+}, function($ctx4) {$ctx4.fillBlock({category:category,methods:methods},$ctx1)})}));
 return _st(_st(_st(map)._keys())._sorted_((function(a,b){
 return smalltalk.withContext(function($ctx4) {
return _st(a).__lt_eq(b);
 }, function($ctx4) {$ctx4.fillBlock({a:a,b:b},$ctx1)})})))._do_((function(category){
@@ -639,10 +618,8 @@ return smalltalk.withContext(function($ctx4) {
methods=_st(map)._at_(category);
 methods;
 return _st(self)._exportMethods_category_of_on_(methods,category,aClass,aStream);
 }, function($ctx4) {$ctx4.fillBlock({category:category,methods:methods},$ctx1)})}));
-}, function($ctx3) {$ctx3.fillBlock({aClass:aClass},$ctx1)})});
-return _st($3)._do_($4);
-}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})});
-_st($1)._do_($2);
+}, function($ctx3) {$ctx3.fillBlock({aClass:aClass},$ctx1)})}));
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
 return self}, function($ctx1) {$ctx1.fill(self,"exportPackageExtensionsOf:on:",{package_:package_,aStream:aStream,name:name,map:map}, smalltalk.ChunkExporter)})},
 args: ["package", "aStream"],
 source: "exportPackageExtensionsOf: package on: aStream\x0a\x09\x22We need to override this one too since we need to group\x0a\x09all methods in a given protocol under a leading methodsFor: chunk\x0a\x09for that class.\x22\x0a\x0a\x09\x22Issue #143: sort protocol alphabetically\x22\x0a\x0a\x09| name map |\x0a\x09name := package name.\x0a\x09(Package sortedClasses: Smalltalk current classes) do: [:each |\x0a\x09\x09{each. each class} do: [:aClass |\x0a\x09\x09\x09map := Dictionary new.\x0a\x09\x09\x09aClass protocolsDo: [:category :methods | \x0a\x09\x09\x09\x09(category match: '^\x5c*', name) ifTrue: [ map at: category put: methods ]].\x0a\x09\x09\x09(map keys sorted: [:a :b | a <= b ]) do: [:category | | methods |\x0a\x09\x09\x09\x09methods := map at: category.\x09\x0a\x09\x09\x09\x09self exportMethods: methods category: category of: aClass on: aStream ]]]",
@@ -729,33 +706,29 @@ category: 'fileIn',
 fn: function (aStream){
 var self=this;
 var chunk,result,parser,lastEmpty;
-return smalltalk.withContext(function($ctx1) { 
var $1,$3,$4,$6,$5,$2;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2;
 parser=_st((smalltalk.ChunkParser || ChunkParser))._on_(aStream);
 lastEmpty=false;
-$1=(function(){
+_st((function(){
 return smalltalk.withContext(function($ctx2) {
chunk=_st(parser)._nextChunk();
 chunk;
 return _st(chunk)._isNil();
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-$2=(function(){
-return smalltalk.withContext(function($ctx2) {
$3=_st(chunk)._isEmpty();
-$4=(function(){
-return smalltalk.withContext(function($ctx3) {
lastEmpty=true;
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}))._whileFalse_((function(){
+return smalltalk.withContext(function($ctx2) {
$1=_st(chunk)._isEmpty();
+if(smalltalk.assert($1)){
+lastEmpty=true;
 return lastEmpty;
-}, function($ctx3) {$ctx3.fillBlock({},$ctx1)})});
-$5=(function(){
-return smalltalk.withContext(function($ctx3) {
result=_st(_st((smalltalk.Compiler || Compiler))._new())._evaluateExpression_(chunk);
+} else {
+result=_st(_st((smalltalk.Compiler || Compiler))._new())._evaluateExpression_(chunk);
 result;
-$6=lastEmpty;
-if(smalltalk.assert($6)){
+$2=lastEmpty;
+if(smalltalk.assert($2)){
 lastEmpty=false;
 lastEmpty;
 return _st(result)._scanFrom_(parser);
 };
-}, function($ctx3) {$ctx3.fillBlock({},$ctx1)})});
-return _st($3)._ifTrue_ifFalse_($4,$5);
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-_st($1)._whileFalse_($2);
+};
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
 return self}, function($ctx1) {$ctx1.fill(self,"import:",{aStream:aStream,chunk:chunk,result:result,parser:parser,lastEmpty:lastEmpty}, smalltalk.Importer)})},
 args: ["aStream"],
 source: "import: aStream\x0a    | chunk result parser lastEmpty |\x0a    parser := ChunkParser on: aStream.\x0a    lastEmpty := false.\x0a    [chunk := parser nextChunk.\x0a     chunk isNil] whileFalse: [\x0a        chunk isEmpty\x0a       \x09\x09ifTrue: [lastEmpty := true]\x0a       \x09\x09ifFalse: [\x0a        \x09\x09result := Compiler new evaluateExpression: chunk.\x0a        \x09\x09lastEmpty \x0a            \x09\x09\x09ifTrue: [\x0a                                  \x09lastEmpty := false.\x0a                                  \x09result scanFrom: parser]]]",
@@ -795,23 +768,16 @@ category: 'laoding',
 fn: function (packageName,aString){
 var self=this;
 var url;
-return smalltalk.withContext(function($ctx1) { 
var $1,$2,$4,$5,$8,$7,$6,$3;
+return smalltalk.withContext(function($ctx1) { 
var $1;
 url=_st(_st(_st(_st("/").__comma(aString)).__comma("/js/")).__comma(packageName)).__comma(".js");
-$1=jQuery;
-$2=url;
-$4=_st("type").__minus_gt("GET");
-$5=_st("dataType").__minus_gt("script");
-$7=(function(jqXHR,textStatus){
-return smalltalk.withContext(function($ctx2) {
$8=_st(_st(jqXHR)._readyState()).__eq((4));
-if(smalltalk.assert($8)){
+_st(jQuery)._ajax_options_(url,smalltalk.HashedCollection._fromPairs_([_st("type").__minus_gt("GET"),_st("dataType").__minus_gt("script"),_st("complete").__minus_gt((function(jqXHR,textStatus){
+return smalltalk.withContext(function($ctx2) {
$1=_st(_st(jqXHR)._readyState()).__eq((4));
+if(smalltalk.assert($1)){
 return _st(self)._initializePackageNamed_prefix_(packageName,aString);
 };
-}, function($ctx2) {$ctx2.fillBlock({jqXHR:jqXHR,textStatus:textStatus},$ctx1)})});
-$6=_st("complete").__minus_gt($7);
-$3=smalltalk.HashedCollection._fromPairs_([$4,$5,$6,_st("error").__minus_gt((function(){
+}, function($ctx2) {$ctx2.fillBlock({jqXHR:jqXHR,textStatus:textStatus},$ctx1)})})),_st("error").__minus_gt((function(){
 return smalltalk.withContext(function($ctx2) {
return _st(window)._alert_(_st("Could not load package at:  ").__comma(url));
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}))]);
-_st($1)._ajax_options_($2,$3);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}))]));
 return self}, function($ctx1) {$ctx1.fill(self,"loadPackage:prefix:",{packageName:packageName,aString:aString,url:url}, smalltalk.PackageLoader)})},
 args: ["packageName", "aString"],
 source: "loadPackage: packageName prefix: aString\x09\x0a\x09| url |\x0a    url := '/', aString, '/js/', packageName, '.js'.\x0a\x09jQuery \x0a\x09\x09ajax: url\x0a        options: #{\x0a\x09\x09\x09'type' -> 'GET'.\x0a\x09\x09\x09'dataType' -> 'script'.\x0a    \x09\x09'complete' -> [ :jqXHR :textStatus | \x0a\x09\x09\x09\x09jqXHR readyState = 4 \x0a                \x09ifTrue: [ self initializePackageNamed: packageName prefix: aString ] ].\x0a\x09\x09\x09'error' -> [ window alert: 'Could not load package at:  ', url ]\x0a\x09\x09}",

+ 28 - 25
js/Kernel-Classes.deploy.js

@@ -271,23 +271,21 @@ selector: "lookupSelector:",
 fn: function (selector){
 var self=this;
 var lookupClass;
-return smalltalk.withContext(function($ctx1) { 
var $1,$3,$4,$2;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2;
 var $early={};
 try {
 lookupClass=self;
-$1=(function(){
+_st((function(){
 return smalltalk.withContext(function($ctx2) {
return _st(lookupClass).__eq(nil);
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-$2=(function(){
-return smalltalk.withContext(function($ctx2) {
$3=_st(lookupClass)._includesSelector_(selector);
-if(smalltalk.assert($3)){
-$4=_st(lookupClass)._methodAt_(selector);
-throw $early=[$4];
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}))._whileFalse_((function(){
+return smalltalk.withContext(function($ctx2) {
$1=_st(lookupClass)._includesSelector_(selector);
+if(smalltalk.assert($1)){
+$2=_st(lookupClass)._methodAt_(selector);
+throw $early=[$2];
 };
 lookupClass=_st(lookupClass)._superclass();
 return lookupClass;
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-_st($1)._whileFalse_($2);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
 return nil;
 }
 catch(e) {if(e===$early)return e[0]; throw e}
@@ -856,17 +854,22 @@ smalltalk.method({
 selector: "addSubclassOf:named:instanceVariableNames:package:",
 fn: function (aClass,aString,aCollection,packageName){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3;
-$1=_st(_st((smalltalk.Smalltalk || Smalltalk))._current())._at_(aString);
+var theClass;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3,$4;
+theClass=_st(_st((smalltalk.Smalltalk || Smalltalk))._current())._at_(aString);
+$1=theClass;
 if(($receiver = $1) == nil || $receiver == undefined){
 $1;
 } else {
-$2=_st(self)._migrateClassNamed_superclass_instanceVariableNames_package_(aString,aClass,aCollection,packageName);
-return $2;
-};
-$3=_st(self)._basicAddSubclassOf_named_instanceVariableNames_package_(aClass,aString,aCollection,packageName);
+$2=_st(_st(theClass)._superclass()).__eq_eq(aClass);
+if(! smalltalk.assert($2)){
+$3=_st(self)._migrateClassNamed_superclass_instanceVariableNames_package_(aString,aClass,aCollection,packageName);
 return $3;
-}, function($ctx1) {$ctx1.fill(self,"addSubclassOf:named:instanceVariableNames:package:",{aClass:aClass,aString:aString,aCollection:aCollection,packageName:packageName}, smalltalk.ClassBuilder)})}
+};
+};
+$4=_st(self)._basicAddSubclassOf_named_instanceVariableNames_package_(aClass,aString,aCollection,packageName);
+return $4;
+}, function($ctx1) {$ctx1.fill(self,"addSubclassOf:named:instanceVariableNames:package:",{aClass:aClass,aString:aString,aCollection:aCollection,packageName:packageName,theClass:theClass}, smalltalk.ClassBuilder)})}
 }),
 smalltalk.ClassBuilder);
 
@@ -1026,7 +1029,8 @@ smalltalk.method({
 selector: "migrateClass:superclass:",
 fn: function (aClass,anotherClass){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
_st(self)._migrateClassNamed_superclass_instanceVariableNames_package_(_st(aClass)._name(),anotherClass,_st(aClass)._instanceVariableNames(),_st(_st(aClass)._package())._name());
+return smalltalk.withContext(function($ctx1) { 
_st(console)._log_(_st(aClass)._name());
+_st(self)._migrateClassNamed_superclass_instanceVariableNames_package_(_st(aClass)._name(),anotherClass,_st(aClass)._instanceVariableNames(),_st(_st(aClass)._package())._name());
 return self}, function($ctx1) {$ctx1.fill(self,"migrateClass:superclass:",{aClass:aClass,anotherClass:anotherClass}, smalltalk.ClassBuilder)})}
 }),
 smalltalk.ClassBuilder);
@@ -1039,6 +1043,7 @@ fn: function (aString,aClass,aCollection,packageName){
 var self=this;
 var oldClass,newClass;
 return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3;
+_st(console)._log_(_st("*** MIGRATING ").__comma(aString));
 oldClass=_st(_st((smalltalk.Smalltalk || Smalltalk))._current())._at_(aString);
 _st(self)._basicRenameClass_to_(oldClass,_st("Old").__comma(aString));
 newClass=_st(self)._addSubclassOf_named_instanceVariableNames_package_(aClass,aString,aCollection,packageName);
@@ -1250,19 +1255,17 @@ selector: "getNodesFrom:",
 fn: function (aCollection){
 var self=this;
 var children,others;
-return smalltalk.withContext(function($ctx1) { 
var $1,$3,$2;
+return smalltalk.withContext(function($ctx1) { 
var $1;
 children=[];
 others=[];
-$1=aCollection;
-$2=(function(each){
-return smalltalk.withContext(function($ctx2) {
$3=_st(_st(each)._superclass()).__eq(_st(self)._theClass());
-if(smalltalk.assert($3)){
+_st(aCollection)._do_((function(each){
+return smalltalk.withContext(function($ctx2) {
$1=_st(_st(each)._superclass()).__eq(_st(self)._theClass());
+if(smalltalk.assert($1)){
 return _st(children)._add_(each);
 } else {
 return _st(others)._add_(each);
 };
-}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})});
-_st($1)._do_($2);
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
 self["@nodes"]=_st(children)._collect_((function(each){
 return smalltalk.withContext(function($ctx2) {
return _st((smalltalk.ClassSorterNode || ClassSorterNode))._on_classes_level_(each,others,_st(_st(self)._level()).__plus((1)));
 }, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));

+ 34 - 31
js/Kernel-Classes.js

@@ -363,23 +363,21 @@ category: 'accessing',
 fn: function (selector){
 var self=this;
 var lookupClass;
-return smalltalk.withContext(function($ctx1) { 
var $1,$3,$4,$2;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2;
 var $early={};
 try {
 lookupClass=self;
-$1=(function(){
+_st((function(){
 return smalltalk.withContext(function($ctx2) {
return _st(lookupClass).__eq(nil);
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-$2=(function(){
-return smalltalk.withContext(function($ctx2) {
$3=_st(lookupClass)._includesSelector_(selector);
-if(smalltalk.assert($3)){
-$4=_st(lookupClass)._methodAt_(selector);
-throw $early=[$4];
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}))._whileFalse_((function(){
+return smalltalk.withContext(function($ctx2) {
$1=_st(lookupClass)._includesSelector_(selector);
+if(smalltalk.assert($1)){
+$2=_st(lookupClass)._methodAt_(selector);
+throw $early=[$2];
 };
 lookupClass=_st(lookupClass)._superclass();
 return lookupClass;
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-_st($1)._whileFalse_($2);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
 return nil;
 }
 catch(e) {if(e===$early)return e[0]; throw e}
@@ -1151,20 +1149,25 @@ selector: "addSubclassOf:named:instanceVariableNames:package:",
 category: 'private',
 fn: function (aClass,aString,aCollection,packageName){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3;
-$1=_st(_st((smalltalk.Smalltalk || Smalltalk))._current())._at_(aString);
+var theClass;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3,$4;
+theClass=_st(_st((smalltalk.Smalltalk || Smalltalk))._current())._at_(aString);
+$1=theClass;
 if(($receiver = $1) == nil || $receiver == undefined){
 $1;
 } else {
-$2=_st(self)._migrateClassNamed_superclass_instanceVariableNames_package_(aString,aClass,aCollection,packageName);
-return $2;
-};
-$3=_st(self)._basicAddSubclassOf_named_instanceVariableNames_package_(aClass,aString,aCollection,packageName);
+$2=_st(_st(theClass)._superclass()).__eq_eq(aClass);
+if(! smalltalk.assert($2)){
+$3=_st(self)._migrateClassNamed_superclass_instanceVariableNames_package_(aString,aClass,aCollection,packageName);
 return $3;
-}, function($ctx1) {$ctx1.fill(self,"addSubclassOf:named:instanceVariableNames:package:",{aClass:aClass,aString:aString,aCollection:aCollection,packageName:packageName}, smalltalk.ClassBuilder)})},
+};
+};
+$4=_st(self)._basicAddSubclassOf_named_instanceVariableNames_package_(aClass,aString,aCollection,packageName);
+return $4;
+}, function($ctx1) {$ctx1.fill(self,"addSubclassOf:named:instanceVariableNames:package:",{aClass:aClass,aString:aString,aCollection:aCollection,packageName:packageName,theClass:theClass}, smalltalk.ClassBuilder)})},
 args: ["aClass", "aString", "aCollection", "packageName"],
-source: "addSubclassOf: aClass named: aString instanceVariableNames: aCollection package: packageName\x0a\x09\x0a    (Smalltalk current at: aString) ifNotNil: [ \x0a    \x09^ self \x0a        \x09migrateClassNamed: aString \x0a            superclass: aClass \x0a            instanceVariableNames: aCollection \x0a            package: packageName ].\x0a\x0a\x09^ self \x0a    \x09basicAddSubclassOf: aClass \x0a        named: aString \x0a        instanceVariableNames: aCollection \x0a        package: packageName",
-messageSends: ["ifNotNil:", "migrateClassNamed:superclass:instanceVariableNames:package:", "at:", "current", "basicAddSubclassOf:named:instanceVariableNames:package:"],
+source: "addSubclassOf: aClass named: aString instanceVariableNames: aCollection package: packageName\x0a    | theClass |\x0a    \x0a    theClass := Smalltalk current at: aString.\x0a    \x0a   \x09theClass ifNotNil: [ \x0a    \x09theClass superclass == aClass ifFalse: [\x0a    \x09\x09^ self \x0a        \x09\x09migrateClassNamed: aString \x0a           \x09 \x09superclass: aClass \x0a           \x09 \x09instanceVariableNames: aCollection \x0a            \x09package: packageName ] ].\x0a\x0a\x09^ self \x0a    \x09basicAddSubclassOf: aClass \x0a        named: aString \x0a        instanceVariableNames: aCollection \x0a        package: packageName",
+messageSends: ["at:", "current", "ifNotNil:", "ifFalse:", "migrateClassNamed:superclass:instanceVariableNames:package:", "==", "superclass", "basicAddSubclassOf:named:instanceVariableNames:package:"],
 referencedClasses: ["Smalltalk"]
 }),
 smalltalk.ClassBuilder);
@@ -1376,11 +1379,12 @@ selector: "migrateClass:superclass:",
 category: 'private',
 fn: function (aClass,anotherClass){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
_st(self)._migrateClassNamed_superclass_instanceVariableNames_package_(_st(aClass)._name(),anotherClass,_st(aClass)._instanceVariableNames(),_st(_st(aClass)._package())._name());
+return smalltalk.withContext(function($ctx1) { 
_st(console)._log_(_st(aClass)._name());
+_st(self)._migrateClassNamed_superclass_instanceVariableNames_package_(_st(aClass)._name(),anotherClass,_st(aClass)._instanceVariableNames(),_st(_st(aClass)._package())._name());
 return self}, function($ctx1) {$ctx1.fill(self,"migrateClass:superclass:",{aClass:aClass,anotherClass:anotherClass}, smalltalk.ClassBuilder)})},
 args: ["aClass", "anotherClass"],
-source: "migrateClass: aClass superclass: anotherClass\x0a\x09self \x0a    \x09migrateClassNamed: aClass name\x0a        superclass: anotherClass\x0a        instanceVariableNames: aClass instanceVariableNames\x0a        package: aClass package name",
-messageSends: ["migrateClassNamed:superclass:instanceVariableNames:package:", "name", "instanceVariableNames", "package"],
+source: "migrateClass: aClass superclass: anotherClass\x0a\x09console log: aClass name.\x0a\x09self \x0a    \x09migrateClassNamed: aClass name\x0a        superclass: anotherClass\x0a        instanceVariableNames: aClass instanceVariableNames\x0a        package: aClass package name",
+messageSends: ["log:", "name", "migrateClassNamed:superclass:instanceVariableNames:package:", "instanceVariableNames", "package"],
 referencedClasses: []
 }),
 smalltalk.ClassBuilder);
@@ -1394,6 +1398,7 @@ fn: function (aString,aClass,aCollection,packageName){
 var self=this;
 var oldClass,newClass;
 return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3;
+_st(console)._log_(_st("*** MIGRATING ").__comma(aString));
 oldClass=_st(_st((smalltalk.Smalltalk || Smalltalk))._current())._at_(aString);
 _st(self)._basicRenameClass_to_(oldClass,_st("Old").__comma(aString));
 newClass=_st(self)._addSubclassOf_named_instanceVariableNames_package_(aClass,aString,aCollection,packageName);
@@ -1414,8 +1419,8 @@ $3=newClass;
 return $3;
 }, function($ctx1) {$ctx1.fill(self,"migrateClassNamed:superclass:instanceVariableNames:package:",{aString:aString,aClass:aClass,aCollection:aCollection,packageName:packageName,oldClass:oldClass,newClass:newClass}, smalltalk.ClassBuilder)})},
 args: ["aString", "aClass", "aCollection", "packageName"],
-source: "migrateClassNamed: aString superclass: aClass instanceVariableNames: aCollection package: packageName\x0a\x09| oldClass newClass |\x0a    \x0a    oldClass := Smalltalk current at: aString.\x0a    \x0a    \x22Rename the old class for existing instances\x22\x0a\x09self basicRenameClass: oldClass to: 'Old', aString.\x0a    \x0a    newClass := self \x0a\x09\x09addSubclassOf: aClass\x0a\x09\x09named: aString \x0a\x09\x09instanceVariableNames: aCollection\x0a\x09\x09package: packageName.\x0a\x0a\x09oldClass subclasses do: [ :each |\x0a    \x09self migrateClass: each superclass: newClass ].\x0a\x0a    [ self copyClass: oldClass to: newClass ] \x0a    \x09on: Error\x0a        do: [ :exception |\x0a        \x09self \x0a            \x09basicRemoveClass: newClass;\x0a            \x09basicRenameClass: oldClass to: aString.\x0a            exception signal ].\x0a            \x0a    self basicRemoveClass: oldClass.\x0a\x09^newClass",
-messageSends: ["at:", "current", "basicRenameClass:to:", ",", "addSubclassOf:named:instanceVariableNames:package:", "do:", "migrateClass:superclass:", "subclasses", "on:do:", "basicRemoveClass:", "signal", "copyClass:to:"],
+source: "migrateClassNamed: aString superclass: aClass instanceVariableNames: aCollection package: packageName\x0a\x09| oldClass newClass |\x0a    \x0a    console log: '*** MIGRATING ', aString.\x0a    \x0a    oldClass := Smalltalk current at: aString.\x0a    \x0a    \x22Rename the old class for existing instances\x22\x0a\x09self basicRenameClass: oldClass to: 'Old', aString.\x0a    \x0a    newClass := self \x0a\x09\x09addSubclassOf: aClass\x0a\x09\x09named: aString \x0a\x09\x09instanceVariableNames: aCollection\x0a\x09\x09package: packageName.\x0a\x0a\x09oldClass subclasses do: [ :each |\x0a    \x09self migrateClass: each superclass: newClass ].\x0a\x0a    [ self copyClass: oldClass to: newClass ] \x0a    \x09on: Error\x0a        do: [ :exception |\x0a        \x09self \x0a            \x09basicRemoveClass: newClass;\x0a            \x09basicRenameClass: oldClass to: aString.\x0a            exception signal ].\x0a            \x0a    self basicRemoveClass: oldClass.\x0a\x09^newClass",
+messageSends: ["log:", ",", "at:", "current", "basicRenameClass:to:", "addSubclassOf:named:instanceVariableNames:package:", "do:", "migrateClass:superclass:", "subclasses", "on:do:", "basicRemoveClass:", "signal", "copyClass:to:"],
 referencedClasses: ["Smalltalk", "Error"]
 }),
 smalltalk.ClassBuilder);
@@ -1672,19 +1677,17 @@ category: 'accessing',
 fn: function (aCollection){
 var self=this;
 var children,others;
-return smalltalk.withContext(function($ctx1) { 
var $1,$3,$2;
+return smalltalk.withContext(function($ctx1) { 
var $1;
 children=[];
 others=[];
-$1=aCollection;
-$2=(function(each){
-return smalltalk.withContext(function($ctx2) {
$3=_st(_st(each)._superclass()).__eq(_st(self)._theClass());
-if(smalltalk.assert($3)){
+_st(aCollection)._do_((function(each){
+return smalltalk.withContext(function($ctx2) {
$1=_st(_st(each)._superclass()).__eq(_st(self)._theClass());
+if(smalltalk.assert($1)){
 return _st(children)._add_(each);
 } else {
 return _st(others)._add_(each);
 };
-}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})});
-_st($1)._do_($2);
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
 self["@nodes"]=_st(children)._collect_((function(each){
 return smalltalk.withContext(function($ctx2) {
return _st((smalltalk.ClassSorterNode || ClassSorterNode))._on_classes_level_(each,others,_st(_st(self)._level()).__plus((1)));
 }, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));

文件差异内容过多而无法显示
+ 144 - 152
js/Kernel-Collections.deploy.js


文件差异内容过多而无法显示
+ 144 - 152
js/Kernel-Collections.js


+ 9 - 8
js/Kernel-Exceptions.deploy.js

@@ -357,17 +357,18 @@ smalltalk.method({
 selector: "logErrorContext:",
 fn: function (aContext){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$3,$2;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2;
 $1=aContext;
-$2=(function(){
-return smalltalk.withContext(function($ctx2) {
$3=_st(aContext)._home();
-if(($receiver = $3) == nil || $receiver == undefined){
-return $3;
+if(($receiver = $1) == nil || $receiver == undefined){
+$1;
+} else {
+$2=_st(aContext)._home();
+if(($receiver = $2) == nil || $receiver == undefined){
+$2;
 } else {
-return _st(self)._logContext_(_st(aContext)._home());
+_st(self)._logContext_(_st(aContext)._home());
+};
 };
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-_st($1)._ifNotNil_($2);
 return self}, function($ctx1) {$ctx1.fill(self,"logErrorContext:",{aContext:aContext}, smalltalk.ErrorHandler)})}
 }),
 smalltalk.ErrorHandler);

+ 9 - 8
js/Kernel-Exceptions.js

@@ -498,17 +498,18 @@ selector: "logErrorContext:",
 category: 'private',
 fn: function (aContext){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$3,$2;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2;
 $1=aContext;
-$2=(function(){
-return smalltalk.withContext(function($ctx2) {
$3=_st(aContext)._home();
-if(($receiver = $3) == nil || $receiver == undefined){
-return $3;
+if(($receiver = $1) == nil || $receiver == undefined){
+$1;
+} else {
+$2=_st(aContext)._home();
+if(($receiver = $2) == nil || $receiver == undefined){
+$2;
 } else {
-return _st(self)._logContext_(_st(aContext)._home());
+_st(self)._logContext_(_st(aContext)._home());
+};
 };
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-_st($1)._ifNotNil_($2);
 return self}, function($ctx1) {$ctx1.fill(self,"logErrorContext:",{aContext:aContext}, smalltalk.ErrorHandler)})},
 args: ["aContext"],
 source: "logErrorContext: aContext\x0a\x09aContext ifNotNil: [\x0a\x09\x09aContext home ifNotNil: [\x0a\x09\x09\x09self logContext: aContext home]]",

+ 5 - 5
js/Kernel-Methods.js

@@ -1016,7 +1016,7 @@ smalltalk.Message.klass);
 
 
 smalltalk.addClass('MethodContext', smalltalk.Object, [], 'Kernel-Methods');
-smalltalk.MethodContext.comment="MethodContext holds all the dynamic state associated with the execution of either a method activation resulting from a message send. That is used to build the call stack while debugging.\x0a  \x0aMethodContext instances are JavaScript `SmalltalkMethodContext` objects defined in boot.js "
+smalltalk.MethodContext.comment="MethodContext holds all the dynamic state associated with the execution of either a method activation resulting from a message send. That is used to build the call stack while debugging.\x0a  \x0aMethodContext instances are JavaScript `SmalltalkMethodContext` objects defined in boot.js"
 smalltalk.addMethod(
 "_asString",
 smalltalk.method({
@@ -1256,7 +1256,7 @@ return smalltalk.withContext(function($ctx1) {
 	;
 return self}, function($ctx1) {$ctx1.fill(self,"constructor:",{aString:aString}, smalltalk.NativeFunction.klass)})},
 args: ["aString"],
-source: "constructor: aString\x0a\x09<\x0a    \x09var native=eval(aString); \x0a        return new native();\x0a\x09>\x0a",
+source: "constructor: aString\x0a\x09<\x0a    \x09var native=eval(aString); \x0a        return new native();\x0a\x09>",
 messageSends: [],
 referencedClasses: []
 }),
@@ -1275,7 +1275,7 @@ return smalltalk.withContext(function($ctx1) {
 	;
 return self}, function($ctx1) {$ctx1.fill(self,"constructor:value:",{aString:aString,anObject:anObject}, smalltalk.NativeFunction.klass)})},
 args: ["aString", "anObject"],
-source: "constructor: aString value:anObject\x0a\x09<\x0a    \x09var native=eval(aString); \x0a        return new native(anObject);\x0a\x09>\x0a",
+source: "constructor: aString value:anObject\x0a\x09<\x0a    \x09var native=eval(aString); \x0a        return new native(anObject);\x0a\x09>",
 messageSends: [],
 referencedClasses: []
 }),
@@ -1294,7 +1294,7 @@ return smalltalk.withContext(function($ctx1) {
 	;
 return self}, function($ctx1) {$ctx1.fill(self,"constructor:value:value:",{aString:aString,anObject:anObject,anObject2:anObject2}, smalltalk.NativeFunction.klass)})},
 args: ["aString", "anObject", "anObject2"],
-source: "constructor: aString value:anObject value: anObject2\x0a\x09<\x0a    \x09var native=eval(aString); \x0a        return new native(anObject,anObject2);\x0a\x09>\x0a",
+source: "constructor: aString value:anObject value: anObject2\x0a\x09<\x0a    \x09var native=eval(aString); \x0a        return new native(anObject,anObject2);\x0a\x09>",
 messageSends: [],
 referencedClasses: []
 }),
@@ -1313,7 +1313,7 @@ return smalltalk.withContext(function($ctx1) {
 	;
 return self}, function($ctx1) {$ctx1.fill(self,"constructor:value:value:value:",{aString:aString,anObject:anObject,anObject2:anObject2,anObject3:anObject3}, smalltalk.NativeFunction.klass)})},
 args: ["aString", "anObject", "anObject2", "anObject3"],
-source: "constructor: aString value:anObject value: anObject2 value:anObject3\x0a\x09<\x0a    \x09var native=eval(aString); \x0a        return new native(anObject,anObject2, anObject3);\x0a\x09>\x0a",
+source: "constructor: aString value:anObject value: anObject2 value:anObject3\x0a\x09<\x0a    \x09var native=eval(aString); \x0a        return new native(anObject,anObject2, anObject3);\x0a\x09>",
 messageSends: [],
 referencedClasses: []
 }),

+ 12 - 16
js/Kernel-Objects.deploy.js

@@ -2724,19 +2724,17 @@ selector: "sortedClasses:",
 fn: function (classes){
 var self=this;
 var children,others,nodes,expandedClasses;
-return smalltalk.withContext(function($ctx1) { 
var $1,$3,$2,$4;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2;
 children=[];
 others=[];
-$1=classes;
-$2=(function(each){
-return smalltalk.withContext(function($ctx2) {
$3=_st(classes)._includes_(_st(each)._superclass());
-if(smalltalk.assert($3)){
+_st(classes)._do_((function(each){
+return smalltalk.withContext(function($ctx2) {
$1=_st(classes)._includes_(_st(each)._superclass());
+if(smalltalk.assert($1)){
 return _st(others)._add_(each);
 } else {
 return _st(children)._add_(each);
 };
-}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})});
-_st($1)._do_($2);
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
 nodes=_st(children)._collect_((function(each){
 return smalltalk.withContext(function($ctx2) {
return _st((smalltalk.ClassSorterNode || ClassSorterNode))._on_classes_level_(each,others,(0));
 }, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
@@ -2747,8 +2745,8 @@ expandedClasses=_st((smalltalk.Array || Array))._new();
 _st(nodes)._do_((function(aNode){
 return smalltalk.withContext(function($ctx2) {
return _st(aNode)._traverseClassesWith_(expandedClasses);
 }, function($ctx2) {$ctx2.fillBlock({aNode:aNode},$ctx1)})}));
-$4=expandedClasses;
-return $4;
+$2=expandedClasses;
+return $2;
 }, function($ctx1) {$ctx1.fill(self,"sortedClasses:",{classes:classes,children:children,others:others,nodes:nodes,expandedClasses:expandedClasses}, smalltalk.Package.klass)})}
 }),
 smalltalk.Package.klass);
@@ -2841,19 +2839,17 @@ smalltalk.method({
 selector: "printString",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $2,$4,$3,$1;
-$2=(smalltalk.String || String);
-$3=(function(stream){
+return smalltalk.withContext(function($ctx1) { 
var $2,$1;
+$1=_st((smalltalk.String || String))._streamContents_((function(stream){
 return smalltalk.withContext(function($ctx2) {
_st(stream)._nextPutAll_(_st(_st(self["@x"])._printString()).__comma("@"));
-$4=_st(_st(self["@y"])._notNil())._and_((function(){
+$2=_st(_st(self["@y"])._notNil())._and_((function(){
 return smalltalk.withContext(function($ctx3) {
return _st(self["@y"])._negative();
 }, function($ctx3) {$ctx3.fillBlock({},$ctx1)})}));
-if(smalltalk.assert($4)){
+if(smalltalk.assert($2)){
 _st(stream)._space();
 };
 return _st(stream)._nextPutAll_(_st(self["@y"])._printString());
-}, function($ctx2) {$ctx2.fillBlock({stream:stream},$ctx1)})});
-$1=_st($2)._streamContents_($3);
+}, function($ctx2) {$ctx2.fillBlock({stream:stream},$ctx1)})}));
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"printString",{}, smalltalk.Point)})}
 }),

+ 12 - 16
js/Kernel-Objects.js

@@ -3766,19 +3766,17 @@ category: 'sorting',
 fn: function (classes){
 var self=this;
 var children,others,nodes,expandedClasses;
-return smalltalk.withContext(function($ctx1) { 
var $1,$3,$2,$4;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2;
 children=[];
 others=[];
-$1=classes;
-$2=(function(each){
-return smalltalk.withContext(function($ctx2) {
$3=_st(classes)._includes_(_st(each)._superclass());
-if(smalltalk.assert($3)){
+_st(classes)._do_((function(each){
+return smalltalk.withContext(function($ctx2) {
$1=_st(classes)._includes_(_st(each)._superclass());
+if(smalltalk.assert($1)){
 return _st(others)._add_(each);
 } else {
 return _st(children)._add_(each);
 };
-}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})});
-_st($1)._do_($2);
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
 nodes=_st(children)._collect_((function(each){
 return smalltalk.withContext(function($ctx2) {
return _st((smalltalk.ClassSorterNode || ClassSorterNode))._on_classes_level_(each,others,(0));
 }, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
@@ -3789,8 +3787,8 @@ expandedClasses=_st((smalltalk.Array || Array))._new();
 _st(nodes)._do_((function(aNode){
 return smalltalk.withContext(function($ctx2) {
return _st(aNode)._traverseClassesWith_(expandedClasses);
 }, function($ctx2) {$ctx2.fillBlock({aNode:aNode},$ctx1)})}));
-$4=expandedClasses;
-return $4;
+$2=expandedClasses;
+return $2;
 }, function($ctx1) {$ctx1.fill(self,"sortedClasses:",{classes:classes,children:children,others:others,nodes:nodes,expandedClasses:expandedClasses}, smalltalk.Package.klass)})},
 args: ["classes"],
 source: "sortedClasses: classes\x0a\x09\x22Answer classes, sorted by superclass/subclasses and by class name for common subclasses (Issue #143)\x22\x0a\x0a\x09| children others nodes expandedClasses |\x0a\x09children := #().\x0a\x09others := #().\x0a\x09classes do: [:each |\x0a\x09\x09(classes includes: each superclass)\x0a\x09\x09\x09ifFalse: [children add: each]\x0a\x09\x09\x09ifTrue: [others add: each]].\x0a\x09nodes := children collect: [:each |\x0a\x09\x09ClassSorterNode on: each classes: others level: 0].\x0a\x09nodes := nodes sorted: [:a :b | a theClass name <= b theClass name ].\x0a\x09expandedClasses := Array new.\x0a\x09nodes do: [:aNode |\x0a\x09\x09aNode traverseClassesWith: expandedClasses].\x0a\x09^expandedClasses",
@@ -3919,19 +3917,17 @@ selector: "printString",
 category: 'printing',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $2,$4,$3,$1;
-$2=(smalltalk.String || String);
-$3=(function(stream){
+return smalltalk.withContext(function($ctx1) { 
var $2,$1;
+$1=_st((smalltalk.String || String))._streamContents_((function(stream){
 return smalltalk.withContext(function($ctx2) {
_st(stream)._nextPutAll_(_st(_st(self["@x"])._printString()).__comma("@"));
-$4=_st(_st(self["@y"])._notNil())._and_((function(){
+$2=_st(_st(self["@y"])._notNil())._and_((function(){
 return smalltalk.withContext(function($ctx3) {
return _st(self["@y"])._negative();
 }, function($ctx3) {$ctx3.fillBlock({},$ctx1)})}));
-if(smalltalk.assert($4)){
+if(smalltalk.assert($2)){
 _st(stream)._space();
 };
 return _st(stream)._nextPutAll_(_st(self["@y"])._printString());
-}, function($ctx2) {$ctx2.fillBlock({stream:stream},$ctx1)})});
-$1=_st($2)._streamContents_($3);
+}, function($ctx2) {$ctx2.fillBlock({stream:stream},$ctx1)})}));
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"printString",{}, smalltalk.Point)})},
 args: [],

+ 23 - 28
js/SUnit.deploy.js

@@ -408,29 +408,27 @@ selector: "execute:",
 fn: function (aBlock){
 var self=this;
 var failed;
-return smalltalk.withContext(function($ctx1) { 
var $1,$3,$4,$2;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2;
 _st(self["@testCase"])._context_(self);
-$1=(function(){
+_st((function(){
 return smalltalk.withContext(function($ctx2) {
failed=true;
 failed;
 _st(aBlock)._value();
 failed=false;
 return failed;
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-$2=(function(){
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}))._ensure_((function(){
 return smalltalk.withContext(function($ctx2) {
_st(self["@testCase"])._context_(nil);
-$3=_st(failed)._and_((function(){
+$1=_st(failed)._and_((function(){
 return smalltalk.withContext(function($ctx3) {
return _st(self["@testCase"])._isAsync();
 }, function($ctx3) {$ctx3.fillBlock({},$ctx1)})}));
-if(smalltalk.assert($3)){
+if(smalltalk.assert($1)){
 _st(self["@testCase"])._finished();
 };
-$4=_st(self["@testCase"])._isAsync();
-if(! smalltalk.assert($4)){
+$2=_st(self["@testCase"])._isAsync();
+if(! smalltalk.assert($2)){
 return _st(self["@testCase"])._tearDown();
 };
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-_st($1)._ensure_($2);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
 return self}, function($ctx1) {$ctx1.fill(self,"execute:",{aBlock:aBlock,failed:failed}, smalltalk.TestContext)})}
 }),
 smalltalk.TestContext);
@@ -485,20 +483,18 @@ smalltalk.method({
 selector: "execute:",
 fn: function (aBlock){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$3,$2;
-$1=(function(){
+return smalltalk.withContext(function($ctx1) { 
var $1;
+_st((function(){
 return smalltalk.withContext(function($ctx2) {
return _st(self)._withErrorReporting_((function(){
 return smalltalk.withContext(function($ctx3) {
return smalltalk.TestContext.fn.prototype._execute_.apply(_st(self), [aBlock]);
 }, function($ctx3) {$ctx3.fillBlock({},$ctx1)})}));
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-$2=(function(){
-return smalltalk.withContext(function($ctx2) {
$3=_st(self["@testCase"])._isAsync();
-if(! smalltalk.assert($3)){
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}))._ensure_((function(){
+return smalltalk.withContext(function($ctx2) {
$1=_st(self["@testCase"])._isAsync();
+if(! smalltalk.assert($1)){
 _st(self["@result"])._increaseRuns();
 return _st(self["@finished"])._value();
 };
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-_st($1)._ensure_($2);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
 return self}, function($ctx1) {$ctx1.fill(self,"execute:",{aBlock:aBlock}, smalltalk.ReportingTestContext)})}
 }),
 smalltalk.ReportingTestContext);
@@ -695,19 +691,18 @@ smalltalk.method({
 selector: "status",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $2,$4,$3,$1;
+return smalltalk.withContext(function($ctx1) { 
var $2,$3,$1;
 $2=_st(_st(self)._errors())._isEmpty();
-$3=(function(){
-return smalltalk.withContext(function($ctx2) {
$4=_st(_st(self)._failures())._isEmpty();
-if(smalltalk.assert($4)){
-return "success";
+if(smalltalk.assert($2)){
+$3=_st(_st(self)._failures())._isEmpty();
+if(smalltalk.assert($3)){
+$1="success";
 } else {
-return "failure";
+$1="failure";
+};
+} else {
+$1="error";
 };
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-$1=_st($2)._ifTrue_ifFalse_($3,(function(){
-return smalltalk.withContext(function($ctx2) {
return "error";
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"status",{}, smalltalk.TestResult)})}
 }),

+ 24 - 29
js/SUnit.js

@@ -556,29 +556,27 @@ category: 'running',
 fn: function (aBlock){
 var self=this;
 var failed;
-return smalltalk.withContext(function($ctx1) { 
var $1,$3,$4,$2;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2;
 _st(self["@testCase"])._context_(self);
-$1=(function(){
+_st((function(){
 return smalltalk.withContext(function($ctx2) {
failed=true;
 failed;
 _st(aBlock)._value();
 failed=false;
 return failed;
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-$2=(function(){
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}))._ensure_((function(){
 return smalltalk.withContext(function($ctx2) {
_st(self["@testCase"])._context_(nil);
-$3=_st(failed)._and_((function(){
+$1=_st(failed)._and_((function(){
 return smalltalk.withContext(function($ctx3) {
return _st(self["@testCase"])._isAsync();
 }, function($ctx3) {$ctx3.fillBlock({},$ctx1)})}));
-if(smalltalk.assert($3)){
+if(smalltalk.assert($1)){
 _st(self["@testCase"])._finished();
 };
-$4=_st(self["@testCase"])._isAsync();
-if(! smalltalk.assert($4)){
+$2=_st(self["@testCase"])._isAsync();
+if(! smalltalk.assert($2)){
 return _st(self["@testCase"])._tearDown();
 };
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-_st($1)._ensure_($2);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
 return self}, function($ctx1) {$ctx1.fill(self,"execute:",{aBlock:aBlock,failed:failed}, smalltalk.TestContext)})},
 args: ["aBlock"],
 source: "execute: aBlock\x0a\x09| failed |\x0a    \x0a    testCase context: self.\x0a    [ \x0a    \x09failed := true. \x0a        aBlock value. \x0a        failed := false \x0a\x09] \x0a    \x09ensure: [\x0a        \x09testCase context: nil.\x0a            \x0a        \x09(failed and: [ testCase isAsync ]) ifTrue: [ \x0a            \x09testCase finished ].\x0a        \x09testCase isAsync ifFalse: [ \x0a        \x09\x09testCase tearDown ] ]",
@@ -654,20 +652,18 @@ selector: "execute:",
 category: 'running',
 fn: function (aBlock){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$3,$2;
-$1=(function(){
+return smalltalk.withContext(function($ctx1) { 
var $1;
+_st((function(){
 return smalltalk.withContext(function($ctx2) {
return _st(self)._withErrorReporting_((function(){
 return smalltalk.withContext(function($ctx3) {
return smalltalk.TestContext.fn.prototype._execute_.apply(_st(self), [aBlock]);
 }, function($ctx3) {$ctx3.fillBlock({},$ctx1)})}));
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-$2=(function(){
-return smalltalk.withContext(function($ctx2) {
$3=_st(self["@testCase"])._isAsync();
-if(! smalltalk.assert($3)){
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}))._ensure_((function(){
+return smalltalk.withContext(function($ctx2) {
$1=_st(self["@testCase"])._isAsync();
+if(! smalltalk.assert($1)){
 _st(self["@result"])._increaseRuns();
 return _st(self["@finished"])._value();
 };
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-_st($1)._ensure_($2);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
 return self}, function($ctx1) {$ctx1.fill(self,"execute:",{aBlock:aBlock}, smalltalk.ReportingTestContext)})},
 args: ["aBlock"],
 source: "execute: aBlock\x0a    [ \x0a    \x09self withErrorReporting: [ super execute: aBlock ] \x0a\x09]\x0a    \x09ensure: [ \x0a        \x09testCase isAsync ifFalse: [ \x0a            \x09result increaseRuns. finished value ] ]",
@@ -905,7 +901,7 @@ return smalltalk.withContext(function($ctx2) {
return _st(self)._addError_(aTest
 }, function($ctx2) {$ctx2.fillBlock({ex:ex},$ctx1)})}));
 return self}, function($ctx1) {$ctx1.fill(self,"runCase:",{aTestCase:aTestCase}, smalltalk.TestResult)})},
 args: ["aTestCase"],
-source: "runCase: aTestCase\x0a\x09[[\x09self increaseRuns.\x0a    \x09aTestCase runCase]\x0a\x09on: TestFailure do: [:ex | self addFailure: aTestCase]]\x0a\x09on: Error do: [:ex | self addError: aTestCase]\x0a",
+source: "runCase: aTestCase\x0a\x09[[\x09self increaseRuns.\x0a    \x09aTestCase runCase]\x0a\x09on: TestFailure do: [:ex | self addFailure: aTestCase]]\x0a\x09on: Error do: [:ex | self addError: aTestCase]",
 messageSends: ["on:do:", "addError:", "addFailure:", "increaseRuns", "runCase"],
 referencedClasses: ["Error", "TestFailure"]
 }),
@@ -936,19 +932,18 @@ selector: "status",
 category: 'accessing',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $2,$4,$3,$1;
+return smalltalk.withContext(function($ctx1) { 
var $2,$3,$1;
 $2=_st(_st(self)._errors())._isEmpty();
-$3=(function(){
-return smalltalk.withContext(function($ctx2) {
$4=_st(_st(self)._failures())._isEmpty();
-if(smalltalk.assert($4)){
-return "success";
+if(smalltalk.assert($2)){
+$3=_st(_st(self)._failures())._isEmpty();
+if(smalltalk.assert($3)){
+$1="success";
 } else {
-return "failure";
+$1="failure";
+};
+} else {
+$1="error";
 };
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
-$1=_st($2)._ifTrue_ifFalse_($3,(function(){
-return smalltalk.withContext(function($ctx2) {
return "error";
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"status",{}, smalltalk.TestResult)})},
 args: [],

+ 1 - 1
js/init.js

@@ -3,5 +3,5 @@ smalltalk.initialize();
 /* Similar to jQuery(document).ready() */
 
 if(this.smalltalkReady) {
-    this.smalltalkReady();
+	this.smalltalkReady();
 }

+ 6 - 0
package.json

@@ -24,5 +24,11 @@
     },
     "scripts": {
         "test": "./test/run_build.sh"
+    },
+    "devDependencies": {
+      "pegjs": "~0.7.0",
+      "grunt": "~0.3.17",
+      "grunt-image-embed": "~0.1.3",
+      "grunt-contrib-mincss": "~0.3.2"
     }
 }

+ 6 - 1
st/Canvas.st

@@ -629,7 +629,8 @@ ensureCurrent
 
 initialize
 	super initialize.
-	self ensureCurrent
+    self isDOMAvailable ifTrue: [
+		self ensureCurrent ]
 ! !
 
 !HTMLSnippet class methodsFor: 'instance creation'!
@@ -638,6 +639,10 @@ current
 	^ current
 !
 
+isDOMAvailable
+	< return typeof document !!== 'undefined' >
+!
+
 new
 	self shouldNotImplement
 ! !

+ 19 - 0
st/Compiler-Exceptions.st

@@ -89,3 +89,22 @@ variableName: aString
 	variableName := aString
 ! !
 
+ErrorHandler subclass: #RethrowErrorHandler
+	instanceVariableNames: ''
+	package: 'Compiler-Exceptions'!
+!RethrowErrorHandler commentStamp!
+This class is used in the commandline version of the compiler.
+It uses the handleError: message of ErrorHandler for printing the stacktrace and throws the error again as JS exception.
+As a result Smalltalk errors are not swallowd by the Amber runtime and compilation can be aborted.!
+
+!RethrowErrorHandler methodsFor: 'error handling'!
+
+basicSignal: anError
+	<throw anError>
+!
+
+handleError: anError
+	super handleError: anError.
+    self basicSignal: anError
+! !
+

+ 1 - 1
st/Compiler-Semantic.st

@@ -422,7 +422,7 @@ errorUnknownVariable: aNode
 	| identifier |
     identifier := aNode value.
     
-	((#('jQuery' 'window' 'process' 'global') includes: identifier) not 
+	((#('jQuery' 'window' 'document' 'process' 'global') includes: identifier) not 
         and: [ self isVariableGloballyUndefined: identifier ]) 
         	ifTrue: [
 				UnknownVariableError new

+ 451 - 451
st/Documentation.st

@@ -1,231 +1,53 @@
 Smalltalk current createPackage: 'Documentation' properties: #{}!
-Object subclass: #DocumentationBuilder
-	instanceVariableNames: 'chapters announcer widget'
-	category: 'Documentation'!
-
-!DocumentationBuilder methodsFor: 'accessing'!
-
-chapters
-	^chapters ifNil: [chapters := self buildChapters]
-!
-
-announcer
-	^announcer ifNil: [announcer := Announcer new]
-!
-
-widget
-	^widget ifNil: [widget := DocumentationWidget on: self]
-! !
-
-!DocumentationBuilder methodsFor: 'building'!
-
-buildChapters
-	^((self class methodDictionary values sorted: [:a :b | a selector < b selector])
-		select: [:each | each category = 'chapters'])
-		collect: [:each | self perform: each selector]
-!
+Object subclass: #ChapterSelectionAnnouncement
+	instanceVariableNames: 'id'
+	package: 'Documentation'!
 
-buildOn: aCanvas
-	aCanvas with: self widget.
-	self 
-		checkHashChange;
-		checkHash
-!
+!ChapterSelectionAnnouncement methodsFor: 'accessing'!
 
-buildOnJQuery: aJQuery
-	self buildOn: (HTMLCanvas onJQuery: aJQuery)
+id
+	^id
 !
 
-build
-	self buildOnJQuery: ('body' asJQuery)
+id: aString
+	id := aString
 ! !
 
-!DocumentationBuilder methodsFor: 'chapters'!
-
-ch1introduction
-	^DocChapter new
-		title: 'Introduction';
-		contents: '
-
-##Amber Smalltalk in a nutshell
-
-Amber is an implementation of the Smalltalk-80 language. It is designed to make client-side web development **faster, easier and more fun** as it allows developers to write HTML5 applications in a live Smalltalk environment!!
-
-Amber is written in itself, including the IDE and the compiler and it runs **directly inside your browser**. The IDE is fairly complete with a class browser, workspace, transcript, unit test runner, object inspectors, cross reference tools and even a debugger.
-
-Noteworthy features:
-
-- Amber is semantically and syntactically very close to [Pharo Smalltalk](http://www.pharo-project.org). Pharo is considered the reference implementation.
-- Amber **seamlessly interacts with JavaScript** and can use its full eco system of libraries without any glue code needed.
-- Amber **has no dependencies** and can be used in any JavaScript runtime, not only inside browsers. An important example is [Node.js](http://nodejs.org).
-- Amber is a live Smalltalk that **compiles incrementally into efficient JavaScript** often mapping one-to-one with JavaScript equivalents.
-- Amber has a **Seaside influenced canvas library** to dynamically generate HTML.
-
-## Arguments for using Amber
-In our humble opinion the main arguments for using Amber are:
-
-- JavaScript is quite a broken language with lots of traps and odd quirks. It is the assembler of the Internet which is cool, but we don''t want to write in it.
-- Smalltalk as a language is immensely cleaner and more mature, both syntactically and semantically.
-- Smalltalk has a simple class model with a lightweight syntax for closures, it is in many ways a perfect match for the Good Parts of JavaScript.
-- Having a true live interactive incremental development environment where you can build your application directly in the browser is unbeatable.
-
-## Disclaimer
-
-This documentation doesn''t aim to teach Smalltalk. 
-Knowledge of Smalltalk is needed to understand the topics covered in this documentation. 
-If you want to learn the Smalltalk language, you can read the excellent [Pharo By Example](http://www.pharobyexample.org) book.
-'
-!
-
-ch2differencesWithOtherSmalltalks
-	^DocChapter new
-		title: 'Differences with other Smalltalks';
-		contents: '
-Amber has some differences with other Smalltalk implementations. This makes porting code a non-trivial thing, but still quite manageable.
-Because it maps Smalltalk constructs one-to-one with the JavaScript equivalent, including Smalltalk classes to JavaScript constructors, the core class library is simplified compared to Pharo Smalltalk.
-And since we want Amber to be useful in building lean browser apps we can''t let it bloat too much.
-
-But apart from missing things other Smalltalks may have, there are also things that are plain different:
-
-- The collection class hierarchy is much simpler compared to most Smalltalk implementations. In part this is because we want to map reasonably well with JavaScript counter parts.
-- As of today, there is no SortedCollection. The size of arrays is dynamic, and they behave like an ordered collection. They can also be sorted with the `#sort*` methods.
-- The `Date` class behaves like the `Date` and `TimeStamp` classes in Pharo Smalltalk. Therefore both `Date today` and `Date now` are valid in Amber.
-- Amber does not have class Character, but `String` does implement some of Character behavior so a single character String can work as a Character.
-- Amber does support **class instance variables**, but not class variables.
-- Amber only has global classes and packages, but not arbitrary objects. Use classes instead like `Smalltalk current` instead of `Smalltalk` etc.
-- Amber does not support pool dictionaries.
-- Amber uses **< ...javascript code... >** to inline JavaScript code and does not have pragmas.
-- Amber does not have class categories. The left side in the browser lists real Packages, but they feel much the same.
-'
-!
-
-ch3GettingStarted
-	^DocChapter new
-		title: 'Getting started';
-		contents: '
-To get started hacking in Amber you can basically take three routes, independent of your platform:
-
-1. Just **try it out directly** at [www.amber-lang.net](http://www.amber-lang.net) - click the **Class browser** button there. But you will **not be able to save any code you write**!! 
-    Still, it works fine for looking at the IDE and playing around. Just **don''t press F5/reload** - it will lose any code you have written.
-2. Download an Amber zip-ball, install [Nodejs](http://www.nodejs.org), fire up the Amber server and then open Amber from localhost - then you **can save code**. Detailed instructions are below!!
-3. Same as above but install git first and get a proper clone from [http://github.com/NicolasPetton/amber](http://github.com/NicolasPetton/amber) instead of a zip/tar-ball. 
-    If you want to **contribute to Amber itself** this is really what you want to do. In fact, in most cases this is what you want to do. It requires installing git first, but it is quite simple - although we leave this bit as an "exercise to the reader" :)
-
-**PLEASE NOTE:** Amber core developers use Linux. 
-We do not want to introduce dependencies that aren''t cross platform - but currently amberc (the command line compiler) is a bash script and we also use Makefiles 
-(for building Amber itself and server side examples) written on Linux/Unix. So using Windows is currently a bit limited - you can''t run "make" in the .st directory to rebuild whole of Amber for example.
- BUT... if you only want to use Amber to build web client apps and not really get involved in hacking Amber itself - then you should be fine!!
-
-## Downloading Amber
-Currently you can download in zip or tar-ball format, either cutting edge or a release. [Downloads are available here](https://github.com/NicolasPetton/amber/archives/amber). 
-
-Unpack wherever you like, but I would rename the directory that is unpacked to something slightly shorter - like say "amber". :)
-And yes, at this point you can double click the index.html file in the amber directory to get the IDE up, but again, **you will not be able to save code**. So please continue below :)
-
-## Installing Node.js
-[Node](http://www.nodejs.org) (for short) is simply the V8 Javascript VM from Google (used in Chrome) hooked together with some hard core C-libraries for doing "evented I/O".
-Basically it''s JavaScript for the server - on asynch steroids. Amber runs fine in Node and we use it for several Amber tools, like amberc (the command line Amber compiler) or the Amber server (see below). 
-There are also several Amber-Node examples to look at if you want to play with running Amber programs server side. **In short - you really want to install Nodejs. :)**
-
-- Installing Node on Linux can be done using your package tool of choice (`apt-get install nodejs` for example) or any other way described at [the download page](http://nodejs.org/#download).
-- Installing Node on MacOS or Windows is probably done best by using the [installers available at Nodejs.org](http://nodejs.org/#download).
-
-## Starting Amber server
-Nicolas has written a minimal webDAV server that is the easiest way to get up and running Amber with the ability to save code. This little server is written in... Amber!! 
-And it runs on top of Node. So to start it up serving your brand new directory tree of sweet Amber you do:
-
-	cd amber	(or whatever you called the directory you unpackaged)
-	./bin/server	(in windows you type `node server\server.js` instead)
-
-It should say it is listening on port 4000. If it does, hooray!! That means both Node and Amber are good. In Windows you might get a question about opening that port in the local firewall - yep, do it!!
-
-## Firing up Amber
-The Amber IDE is written in... Amber. It uses [jQuery](http://jquery.com) and runs right in your browser as a ... well, a web page. 
-We could open it up just using a file url - but the reason we performed the previous steps is so that we can load the IDE web page from a server that can handle PUTs (webDAV) of source code. 
-According to web security Amber can only do PUT back to the same server it was loaded from. Thus we instead want to open it [through our little server now listening on port 4000](http://localhost:4000/index.html).
-Clicking that link and then pressing the **Class browser** should get your Amber IDE running with the ability to commit modified packages locally.
-
-To verify that you can indeed commit now - just select a Package in the browser, like say "Examples" and press the **Commit** button below. **If all goes well nothing happens :)**. 
-So in order to really know if it worked we can check the modified date on the files **amber/st/Examples.st**, **amber/js/Examples.js** and **amber/js/Examples.deploy.js** - they should be brand new.
-
-NOTE: We can use any webDAV server and Apache2 has been used earlier and works fine. But the Amber server is smaller and simpler to start.
-'
-!
-
-ch5Index
-	^ClassesIndexChapter new
-!
-
-ch6KernelObjects
-	^PackageDocChapter on: (Package named: 'Kernel-Objects')
-!
-
-ch7KernelClasses
-	^PackageDocChapter on: (Package named: 'Kernel-Classes')
-!
-
-ch4Tutorials
-	^TutorialsChapter new
-!
-
-ch8KernelCollection
-	^PackageDocChapter on: (Package named: 'Kernel-Collections')
-!
-
-ch9KernelMethods
-	^PackageDocChapter on: (Package named: 'Kernel-Methods')
-! !
+Object subclass: #ClassSelectionAnnouncement
+	instanceVariableNames: 'theClass'
+	package: 'Documentation'!
 
-!DocumentationBuilder methodsFor: 'routing'!
+!ClassSelectionAnnouncement methodsFor: 'accessing'!
 
-checkHashChange
-	(window jQuery: window) bind: 'hashchange' do: [self checkHash]
+theClass
+	^theClass
 !
 
-checkHash
-	| hash presentation |
-	hash := document location hash  replace: '^#' with: ''.
-	self announcer announce: (ChapterSelectionAnnouncement new 
-		id: hash; 
-		yourself)
-! !
-
-!DocumentationBuilder methodsFor: 'updating'!
-
-update
-	chapters := nil.
-	announcer := nil.
-	widget := nil.
-	(window jQuery: '.documentation') remove.
-	self build
-! !
-
-DocumentationBuilder class instanceVariableNames: 'current'!
-
-!DocumentationBuilder class methodsFor: 'accessing'!
-
-current
-	^current ifNil: [current := self new]
+theClass: aClass
+	theClass := aClass
 ! !
 
-!DocumentationBuilder class methodsFor: 'initialization'!
+!ClassSelectionAnnouncement class methodsFor: 'instance creation'!
 
-initialize
-	self current build
+on: aClass
+	^self new
+		theClass: aClass;
+		yourself
 ! !
 
 Widget subclass: #DocChapter
-	instanceVariableNames: 'title contents parent'
-	category: 'Documentation'!
+	instanceVariableNames: 'title contents parent level'
+	package: 'Documentation'!
 
 !DocChapter methodsFor: 'accessing'!
 
-title
-	^title ifNil: ['']
+announcer
+	^DocumentationBuilder current announcer
 !
 
-title: aString
-	title := aString
+chapters
+	"A doc chapter can contain sub chapters"
+	^#()
 !
 
 contents
@@ -236,17 +58,18 @@ contents: aString
 	contents := aString
 !
 
-htmlContents
-	^(Showdown at: #converter) new makeHtml: self contents
+cssClass
+	^'doc_chapter'
 !
 
-chapters
-	"A doc chapter can contain sub chapters"
-	^#()
+htmlContents
+	^(Showdown at: #converter) new makeHtml: self contents
 !
 
-cssClass
-	^'doc_chapter'
+id
+	"The id is used in url fragments. 
+	It must be unique amoung all chapters"
+	^self title replace: ' ' with: '-'
 !
 
 level
@@ -265,28 +88,26 @@ parent: aChapter
 	parent := aChapter
 !
 
-id
-	"The id is used in url fragments. 
-	It must be unique amoung all chapters"
-	^self title replace: ' ' with: '-'
+title
+	^title ifNil: ['']
 !
 
-announcer
-	^DocumentationBuilder current announcer
+title: aString
+	title := aString
 ! !
 
 !DocChapter methodsFor: 'actions'!
 
-selectClass: aClass
-	DocumentationBuilder current announcer announce: (ClassSelectionAnnouncement on: aClass)
+displayChapter: aChapter
+	DocumentationBuilder current widget displayChapter: aChapter
 !
 
 selectChapter: aChapter
 	document location hash: aChapter id
 !
 
-displayChapter: aChapter
-	DocumentationBuilder current widget displayChapter: aChapter
+selectClass: aClass
+	DocumentationBuilder current announcer announce: (ClassSelectionAnnouncement on: aClass)
 ! !
 
 !DocChapter methodsFor: 'initialization'!
@@ -298,14 +119,6 @@ initialize
 
 !DocChapter methodsFor: 'rendering'!
 
-renderOn: html
-	html div 
-		class: self cssClass;
-		with: [
-			self renderDocOn: html.
-			self renderLinksOn: html]
-!
-
 renderDocOn: html
 	| div |
 	html h1 with: self title.
@@ -314,6 +127,17 @@ renderDocOn: html
 	div asJQuery html: self htmlContents
 !
 
+renderLinksOn: html
+	html ul 
+		class: 'links';
+		with: [
+			self chapters do: [:each |
+				html li with: [
+					html a
+						with: each title;
+						onClick: [self selectChapter: each]]]]
+!
+
 renderNavigationOn: html
 	self parent ifNotNil: [
 		html div 
@@ -323,15 +147,12 @@ renderNavigationOn: html
 					onClick: [self selectChapter: self parent]]]
 !
 
-renderLinksOn: html
-	html ul 
-		class: 'links';
+renderOn: html
+	html div 
+		class: self cssClass;
 		with: [
-			self chapters do: [:each |
-				html li with: [
-					html a
-						with: each title;
-						onClick: [self selectChapter: each]]]]
+			self renderDocOn: html.
+			self renderLinksOn: html]
 ! !
 
 !DocChapter methodsFor: 'subscriptions'!
@@ -341,57 +162,12 @@ subscribe
 		ann id = self id ifTrue: [self displayChapter: self]]
 ! !
 
-DocChapter subclass: #PackageDocChapter
-	instanceVariableNames: 'package chapters'
-	category: 'Documentation'!
-
-!PackageDocChapter methodsFor: 'accessing'!
-
-package
-	^package
-!
-
-title
-	^'Package ', self package name
-!
-
-chapters
-	^chapters
-!
-
-contents
-	^'Classes in package ', self package name, ':'
-! !
-
-!PackageDocChapter methodsFor: 'initialization'!
-
-initializeWithPackage: aPackage
-	package := aPackage.
-	chapters := (aPackage classes sorted: [:a :b | a name < b name]) collect: [:each |
-		(ClassDocChapter on: each)
-			parent: self;
-			yourself]
-! !
-
-!PackageDocChapter class methodsFor: 'instance creation'!
-
-on: aPackage
-	^self basicNew
-		initializeWithPackage: aPackage;
-		initialize;
-		yourself
-! !
-
 DocChapter subclass: #ClassDocChapter
 	instanceVariableNames: 'theClass'
-	category: 'Documentation'!
+	package: 'Documentation'!
 
 !ClassDocChapter methodsFor: 'accessing'!
 
-theClass
-	^theClass
-!
-
 contents
 	^self theClass comment isEmpty
 		ifTrue: [self theClass name, ' is not documented yet.']
@@ -402,12 +178,16 @@ cssClass
 	^'doc_class ', super cssClass
 !
 
-title
-	^self theClass name
-!
-
 initializeWithClass: aClass
 	theClass := aClass
+!
+
+theClass
+	^theClass
+!
+
+title
+	^self theClass name
 ! !
 
 !ClassDocChapter methodsFor: 'rendering'!
@@ -440,106 +220,22 @@ on: aClass
 		yourself
 ! !
 
-Widget subclass: #DocumentationWidget
-	instanceVariableNames: 'builder selectedChapter chapterDiv'
-	category: 'Documentation'!
-
-!DocumentationWidget methodsFor: 'accessing'!
-
-builder
-	^builder
-!
-
-builder: aDocumentationBuilder
-	builder := aDocumentationBuilder
-!
-
-chapters
-	^self builder chapters
-!
-
-selectedChapter
-	^selectedChapter ifNil: [selectedChapter := self chapters first]
-!
-
-selectedChapter: aChapter
-	^selectedChapter := aChapter
-! !
-
-!DocumentationWidget methodsFor: 'actions'!
-
-displayChapter: aChapter
-	self selectedChapter: aChapter.
-	self updateChapterDiv
-!
-
-selectChapter: aChapter
-	document location hash: aChapter id
-! !
-
-!DocumentationWidget methodsFor: 'rendering'!
-
-renderOn: html
-	html div 
-		class: 'documentation';
-		with: [
-			self renderMenuOn: html.
-			chapterDiv := html div.
-			self updateChapterDiv]
-!
-
-renderMenuOn: html
-	html div 
-		class: 'menu';
-		with: [
-			html ol with: [
-				self chapters do: [:each |
-					html li with: [
-						self renderChapterMenu: each on: html]]]]
-!
-
-renderChapterMenu: aChapter on: html
-	html a
-		with: aChapter title;
-		onClick: [
-			self selectChapter: aChapter].
-	html ol with: [
-			aChapter chapters do: [:each |
-				html li with: [
-					self renderChapterMenu: each on: html]]]
-! !
-
-!DocumentationWidget methodsFor: 'updating'!
-
-updateChapterDiv
-	chapterDiv contents: [:html |
-		html with: self selectedChapter]
-! !
-
-!DocumentationWidget class methodsFor: 'instance creation'!
-
-on: aBuilder
-	^self new
-		builder: aBuilder;
-		yourself
-! !
-
 DocChapter subclass: #ClassesIndexChapter
 	instanceVariableNames: ''
-	category: 'Documentation'!
+	package: 'Documentation'!
 
 !ClassesIndexChapter methodsFor: 'accessing'!
 
+alphabet
+	^'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+!
+
 cssClass
 	^'index_doc ', super cssClass
 !
 
 title
 	^'Smalltalk classes by index'
-!
-
-alphabet
-	^'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
 ! !
 
 !ClassesIndexChapter methodsFor: 'rendering'!
@@ -557,74 +253,132 @@ renderDocOn: html
 						onClick: [self selectClass: each]]]]]
 ! !
 
-Object subclass: #ClassSelectionAnnouncement
-	instanceVariableNames: 'theClass'
-	category: 'Documentation'!
+DocChapter subclass: #PackageDocChapter
+	instanceVariableNames: 'package chapters'
+	package: 'Documentation'!
 
-!ClassSelectionAnnouncement methodsFor: 'accessing'!
+!PackageDocChapter methodsFor: 'accessing'!
 
-theClass
-	^theClass
+chapters
+	^chapters
 !
 
-theClass: aClass
-	theClass := aClass
-! !
+contents
+	^'Classes in package ', self package name, ':'
+!
 
-!ClassSelectionAnnouncement class methodsFor: 'instance creation'!
+package
+	^package
+!
 
-on: aClass
-	^self new
-		theClass: aClass;
-		yourself
+title
+	^'Package ', self package name
 ! !
 
-Object subclass: #ChapterSelectionAnnouncement
-	instanceVariableNames: 'id'
-	category: 'Documentation'!
+!PackageDocChapter methodsFor: 'initialization'!
 
-!ChapterSelectionAnnouncement methodsFor: 'accessing'!
+initializeWithPackage: aPackage
+	package := aPackage.
+	chapters := (aPackage classes sorted: [:a :b | a name < b name]) collect: [:each |
+		(ClassDocChapter on: each)
+			parent: self;
+			yourself]
+! !
 
-id
-	^id
-!
+!PackageDocChapter class methodsFor: 'instance creation'!
 
-id: aString
-	id := aString
+on: aPackage
+	^self basicNew
+		initializeWithPackage: aPackage;
+		initialize;
+		yourself
 ! !
 
 DocChapter subclass: #TutorialsChapter
 	instanceVariableNames: ''
-	category: 'Documentation'!
+	package: 'Documentation'!
 
 !TutorialsChapter methodsFor: 'accessing'!
 
-title
-	^'Tutorials'
+chapters
+	^{ self firstAppChapter. self counterChapter }
 !
 
 contents
 	^'Here''s a serie of tutorials. If you are new to Smalltalk, you can also learn Amber online with [ProfStef](http://www.amber-lang.net/learn.html)'
 !
 
-chapters
-	^{ self firstAppChapter. self counterChapter }
-!
-
-firstAppChapter
+counterChapter
 	^DocChapter new
-		title: 'A first application';
+		title: 'The counter application';
 		contents: '
 
-Let''s make Hello World in Amber.
+This tutorial will teach you how to build HTML with Amber using jQuery and the HTMLCanvas API. It is freely adapted from 
+the [Seaside counter example](http://www.seaside.st/about/examples/counter)
 
-First, you need a place for your new project. I made a new directory under amber:
+##The counter widget
 
-    amber/projects/hello
+The counter is the most basic example of a widget. It allows to increment and decrement a number by clicking a button.
 
-This will store your project files. To get started, add a new index.html file to this folder, as well as empty js and st folders.
+Amber already comes with a counter example in the `Examples` package. To avoid class name conflict, we''ll name our counter class `TCounter`.
 
-Your index.html can be really basic. The most important thing it does is include amber.js and run loadAmber. Here is a basic index.html you can use:
+    Widget subclass: #TCounter
+        instanceVariableNames: ''count header''
+        package: ''Tutorials''
+
+The first method is used to initialize the component with the default state, in this case we set the counter to 0:
+
+    initialize
+        super initialize.
+        count := 0
+
+The method used for rendering a widget is `#renderOn:`. It takes an instance of HTMLCanvas as parameter. 
+The `header` h1 kept as an instance variable, so when the count value change, we can update it''s contents accordingly.
+
+    renderOn: html
+        header := html h1 
+            with: count asString;
+            yourself.
+        html button
+            with: ''++'';
+            onClick: [self increase].
+        html button
+            with: ''--'';
+            onClick: [self decrease]
+
+The counter is almost ready. All we need now is to implement the two action methods `#increase` and `#decrease` to change the state 
+of our counter and update its header.
+
+    increase
+        count := count + 1.
+        header contents: [:html | html with: count asString]
+
+    decrease
+        count := count - 1.
+        header contents: [:html | html with: count asString]
+
+
+That''s it!! We can now display an instance of TCounter by rendering it on the page using jQuery:
+
+    TCounter new appendToJQuery: ''body'' asJQuery
+
+'
+!
+
+firstAppChapter
+	^DocChapter new
+		title: 'A first application';
+		contents: '
+
+Let''s make Hello World in Amber.
+
+First, you need a place for your new project. I made a new directory under amber:
+
+    amber/projects/hello
+
+This will store your project files. To get started, add a new index.html file to this folder, as well as empty js and st folders.
+
+Your index.html can be really basic. The most important thing it does is include amber.js and run loadAmber. Here is a basic index.html you can use:
 
 
     <!!DOCTYPE html>
@@ -699,60 +453,306 @@ From there, you can create new Smalltalk classes and messages to build up your a
 '
 !
 
-counterChapter
+title
+	^'Tutorials'
+! !
+
+Object subclass: #DocumentationBuilder
+	instanceVariableNames: 'chapters announcer widget'
+	package: 'Documentation'!
+
+!DocumentationBuilder methodsFor: 'accessing'!
+
+announcer
+	^announcer ifNil: [announcer := Announcer new]
+!
+
+chapters
+	^chapters ifNil: [chapters := self buildChapters]
+!
+
+widget
+	^widget ifNil: [widget := DocumentationWidget on: self]
+! !
+
+!DocumentationBuilder methodsFor: 'building'!
+
+build
+	self buildOnJQuery: ('body' asJQuery)
+!
+
+buildChapters
+	^((self class methodDictionary values sorted: [:a :b | a selector < b selector])
+		select: [:each | each category = 'chapters'])
+		collect: [:each | self perform: each selector]
+!
+
+buildOn: aCanvas
+	aCanvas with: self widget.
+	self 
+		checkHashChange;
+		checkHash
+!
+
+buildOnJQuery: aJQuery
+	self buildOn: (HTMLCanvas onJQuery: aJQuery)
+! !
+
+!DocumentationBuilder methodsFor: 'chapters'!
+
+ch1introduction
 	^DocChapter new
-		title: 'The counter application';
+		title: 'Introduction';
 		contents: '
 
-This tutorial will teach you how to build HTML with Amber using jQuery and the HTMLCanvas API. It is freely adapted from 
-the [Seaside counter example](http://www.seaside.st/about/examples/counter)
+##Amber Smalltalk in a nutshell
 
-##The counter widget
+Amber is an implementation of the Smalltalk-80 language. It is designed to make client-side web development **faster, easier and more fun** as it allows developers to write HTML5 applications in a live Smalltalk environment!!
 
-The counter is the most basic example of a widget. It allows to increment and decrement a number by clicking a button.
+Amber is written in itself, including the IDE and the compiler and it runs **directly inside your browser**. The IDE is fairly complete with a class browser, workspace, transcript, unit test runner, object inspectors, cross reference tools and even a debugger.
 
-Amber already comes with a counter example in the `Examples` package. To avoid class name conflict, we''ll name our counter class `TCounter`.
+Noteworthy features:
 
-    Widget subclass: #TCounter
-        instanceVariableNames: ''count header''
-        package: ''Tutorials''
+- Amber is semantically and syntactically very close to [Pharo Smalltalk](http://www.pharo-project.org). Pharo is considered the reference implementation.
+- Amber **seamlessly interacts with JavaScript** and can use its full eco system of libraries without any glue code needed.
+- Amber **has no dependencies** and can be used in any JavaScript runtime, not only inside browsers. An important example is [Node.js](http://nodejs.org).
+- Amber is a live Smalltalk that **compiles incrementally into efficient JavaScript** often mapping one-to-one with JavaScript equivalents.
+- Amber has a **Seaside influenced canvas library** to dynamically generate HTML.
 
-The first method is used to initialize the component with the default state, in this case we set the counter to 0:
+## Arguments for using Amber
+In our humble opinion the main arguments for using Amber are:
 
-    initialize
-        super initialize.
-        count := 0
+- JavaScript is quite a broken language with lots of traps and odd quirks. It is the assembler of the Internet which is cool, but we don''t want to write in it.
+- Smalltalk as a language is immensely cleaner and more mature, both syntactically and semantically.
+- Smalltalk has a simple class model with a lightweight syntax for closures, it is in many ways a perfect match for the Good Parts of JavaScript.
+- Having a true live interactive incremental development environment where you can build your application directly in the browser is unbeatable.
 
-The method used for rendering a widget is `#renderOn:`. It takes an instance of HTMLCanvas as parameter. 
-The `header` h1 kept as an instance variable, so when the count value change, we can update it''s contents accordingly.
+## Disclaimer
 
-    renderOn: html
-        header := html h1 
-            with: count asString;
-            yourself.
-        html button
-            with: ''++'';
-            onClick: [self increase].
-        html button
-            with: ''--'';
-            onClick: [self decrease]
+This documentation doesn''t aim to teach Smalltalk. 
+Knowledge of Smalltalk is needed to understand the topics covered in this documentation. 
+If you want to learn the Smalltalk language, you can read the excellent [Pharo By Example](http://www.pharobyexample.org) book.
+'
+!
 
-The counter is almost ready. All we need now is to implement the two action methods `#increase` and `#decrease` to change the state 
-of our counter and update its header.
+ch2differencesWithOtherSmalltalks
+	^DocChapter new
+		title: 'Differences with other Smalltalks';
+		contents: '
+Amber has some differences with other Smalltalk implementations. This makes porting code a non-trivial thing, but still quite manageable.
+Because it maps Smalltalk constructs one-to-one with the JavaScript equivalent, including Smalltalk classes to JavaScript constructors, the core class library is simplified compared to Pharo Smalltalk.
+And since we want Amber to be useful in building lean browser apps we can''t let it bloat too much.
 
-    increase
-        count := count + 1.
-        header contents: [:html | html with: count asString]
+But apart from missing things other Smalltalks may have, there are also things that are plain different:
 
-    decrease
-        count := count - 1.
-        header contents: [:html | html with: count asString]
+- The collection class hierarchy is much simpler compared to most Smalltalk implementations. In part this is because we want to map reasonably well with JavaScript counter parts.
+- As of today, there is no SortedCollection. The size of arrays is dynamic, and they behave like an ordered collection. They can also be sorted with the `#sort*` methods.
+- The `Date` class behaves like the `Date` and `TimeStamp` classes in Pharo Smalltalk. Therefore both `Date today` and `Date now` are valid in Amber.
+- Amber does not have class Character, but `String` does implement some of Character behavior so a single character String can work as a Character.
+- Amber does support **class instance variables**, but not class variables.
+- Amber only has global classes and packages, but not arbitrary objects. Use classes instead like `Smalltalk current` instead of `Smalltalk` etc.
+- Amber does not support pool dictionaries.
+- Amber uses **< ...javascript code... >** to inline JavaScript code and does not have pragmas.
+- Amber does not have class categories. The left side in the browser lists real Packages, but they feel much the same.
+'
+!
 
+ch3GettingStarted
+	^DocChapter new
+		title: 'Getting started';
+		contents: '
+To get started hacking in Amber you can basically take three routes, independent of your platform:
 
-That''s it!! We can now display an instance of TCounter by rendering it on the page using jQuery:
+1. Just **try it out directly** at [www.amber-lang.net](http://www.amber-lang.net) - click the **Class browser** button there. But you will **not be able to save any code you write**!! 
+    Still, it works fine for looking at the IDE and playing around. Just **don''t press F5/reload** - it will lose any code you have written.
+2. Download an Amber zip-ball, install [Nodejs](http://www.nodejs.org), fire up the Amber server and then open Amber from localhost - then you **can save code**. Detailed instructions are below!!
+3. Same as above but install git first and get a proper clone from [http://github.com/NicolasPetton/amber](http://github.com/NicolasPetton/amber) instead of a zip/tar-ball. 
+    If you want to **contribute to Amber itself** this is really what you want to do. In fact, in most cases this is what you want to do. It requires installing git first, but it is quite simple - although we leave this bit as an "exercise to the reader" :)
 
-    TCounter new appendToJQuery: ''body'' asJQuery
+**PLEASE NOTE:** Amber core developers use Linux. 
+We do not want to introduce dependencies that aren''t cross platform - but currently amberc (the command line compiler) is a bash script and we also use Makefiles 
+(for building Amber itself and server side examples) written on Linux/Unix. So using Windows is currently a bit limited - you can''t run "make" in the .st directory to rebuild whole of Amber for example.
+ BUT... if you only want to use Amber to build web client apps and not really get involved in hacking Amber itself - then you should be fine!!
+
+## Downloading Amber
+Currently you can download in zip or tar-ball format, either cutting edge or a release. [Downloads are available here](https://github.com/NicolasPetton/amber/archives/amber). 
+
+Unpack wherever you like, but I would rename the directory that is unpacked to something slightly shorter - like say "amber". :)
+And yes, at this point you can double click the index.html file in the amber directory to get the IDE up, but again, **you will not be able to save code**. So please continue below :)
+
+## Installing Node.js
+[Node](http://www.nodejs.org) (for short) is simply the V8 Javascript VM from Google (used in Chrome) hooked together with some hard core C-libraries for doing "evented I/O".
+Basically it''s JavaScript for the server - on asynch steroids. Amber runs fine in Node and we use it for several Amber tools, like amberc (the command line Amber compiler) or the Amber server (see below). 
+There are also several Amber-Node examples to look at if you want to play with running Amber programs server side. **In short - you really want to install Nodejs. :)**
 
+- Installing Node on Linux can be done using your package tool of choice (`apt-get install nodejs` for example) or any other way described at [the download page](http://nodejs.org/#download).
+- Installing Node on MacOS or Windows is probably done best by using the [installers available at Nodejs.org](http://nodejs.org/#download).
+
+## Starting Amber server
+Nicolas has written a minimal webDAV server that is the easiest way to get up and running Amber with the ability to save code. This little server is written in... Amber!! 
+And it runs on top of Node. So to start it up serving your brand new directory tree of sweet Amber you do:
+
+	cd amber	(or whatever you called the directory you unpackaged)
+	./bin/server	(in windows you type `node server\server.js` instead)
+
+It should say it is listening on port 4000. If it does, hooray!! That means both Node and Amber are good. In Windows you might get a question about opening that port in the local firewall - yep, do it!!
+
+## Firing up Amber
+The Amber IDE is written in... Amber. It uses [jQuery](http://jquery.com) and runs right in your browser as a ... well, a web page. 
+We could open it up just using a file url - but the reason we performed the previous steps is so that we can load the IDE web page from a server that can handle PUTs (webDAV) of source code. 
+According to web security Amber can only do PUT back to the same server it was loaded from. Thus we instead want to open it [through our little server now listening on port 4000](http://localhost:4000/index.html).
+Clicking that link and then pressing the **Class browser** should get your Amber IDE running with the ability to commit modified packages locally.
+
+To verify that you can indeed commit now - just select a Package in the browser, like say "Examples" and press the **Commit** button below. **If all goes well nothing happens :)**. 
+So in order to really know if it worked we can check the modified date on the files **amber/st/Examples.st**, **amber/js/Examples.js** and **amber/js/Examples.deploy.js** - they should be brand new.
+
+NOTE: We can use any webDAV server and Apache2 has been used earlier and works fine. But the Amber server is smaller and simpler to start.
 '
+!
+
+ch4Tutorials
+	^TutorialsChapter new
+!
+
+ch5Index
+	^ClassesIndexChapter new
+!
+
+ch6KernelObjects
+	^PackageDocChapter on: (Package named: 'Kernel-Objects')
+!
+
+ch7KernelClasses
+	^PackageDocChapter on: (Package named: 'Kernel-Classes')
+!
+
+ch8KernelCollection
+	^PackageDocChapter on: (Package named: 'Kernel-Collections')
+!
+
+ch9KernelMethods
+	^PackageDocChapter on: (Package named: 'Kernel-Methods')
+! !
+
+!DocumentationBuilder methodsFor: 'routing'!
+
+checkHash
+	| hash presentation |
+	hash := document location hash  replace: '^#' with: ''.
+	self announcer announce: (ChapterSelectionAnnouncement new 
+		id: hash; 
+		yourself)
+!
+
+checkHashChange
+	(window jQuery: window) bind: 'hashchange' do: [self checkHash]
+! !
+
+!DocumentationBuilder methodsFor: 'updating'!
+
+update
+	chapters := nil.
+	announcer := nil.
+	widget := nil.
+	(window jQuery: '.documentation') remove.
+	self build
+! !
+
+DocumentationBuilder class instanceVariableNames: 'current'!
+
+!DocumentationBuilder class methodsFor: 'accessing'!
+
+current
+	^current ifNil: [current := self new]
+! !
+
+!DocumentationBuilder class methodsFor: 'initialization'!
+
+initialize
+	self current build
+! !
+
+Widget subclass: #DocumentationWidget
+	instanceVariableNames: 'builder selectedChapter chapterDiv'
+	package: 'Documentation'!
+
+!DocumentationWidget methodsFor: 'accessing'!
+
+builder
+	^builder
+!
+
+builder: aDocumentationBuilder
+	builder := aDocumentationBuilder
+!
+
+chapters
+	^self builder chapters
+!
+
+selectedChapter
+	^selectedChapter ifNil: [selectedChapter := self chapters first]
+!
+
+selectedChapter: aChapter
+	^selectedChapter := aChapter
+! !
+
+!DocumentationWidget methodsFor: 'actions'!
+
+displayChapter: aChapter
+	self selectedChapter: aChapter.
+	self updateChapterDiv
+!
+
+selectChapter: aChapter
+	document location hash: aChapter id
+! !
+
+!DocumentationWidget methodsFor: 'rendering'!
+
+renderChapterMenu: aChapter on: html
+	html a
+		with: aChapter title;
+		onClick: [
+			self selectChapter: aChapter].
+	html ol with: [
+			aChapter chapters do: [:each |
+				html li with: [
+					self renderChapterMenu: each on: html]]]
+!
+
+renderMenuOn: html
+	html div 
+		class: 'menu';
+		with: [
+			html ol with: [
+				self chapters do: [:each |
+					html li with: [
+						self renderChapterMenu: each on: html]]]]
+!
+
+renderOn: html
+	html div 
+		class: 'documentation';
+		with: [
+			self renderMenuOn: html.
+			chapterDiv := html div.
+			self updateChapterDiv]
+! !
+
+!DocumentationWidget methodsFor: 'updating'!
+
+updateChapterDiv
+	chapterDiv contents: [:html |
+		html with: self selectedChapter]
+! !
+
+!DocumentationWidget class methodsFor: 'instance creation'!
+
+on: aBuilder
+	^self new
+		builder: aBuilder;
+		yourself
 ! !
 

+ 1 - 1
st/IDE.st

@@ -1205,7 +1205,7 @@ updateSourceAndButtons
                 currentProtocol := selectedProtocol.
                 (currentProtocol isNil and: [ selectedMethod notNil ])
                 	ifTrue: [ currentProtocol := selectedMethod category].
-				self protocols do: [:each |
+				self protocols do: [:each | | option |
 					option := html option with: each.
 					currentProtocol = each ifTrue: [ option at: 'selected' put: 'selected' ] ]].
 		selectedMethod isNil ifFalse: [

+ 14 - 7
st/Kernel-Classes.st

@@ -449,13 +449,17 @@ superclass: aClass subclass: aString instanceVariableNames: aString2 package: aS
 !ClassBuilder methodsFor: 'private'!
 
 addSubclassOf: aClass named: aString instanceVariableNames: aCollection package: packageName
-	
-    (Smalltalk current at: aString) ifNotNil: [ 
-    	^ self 
-        	migrateClassNamed: aString 
-            superclass: aClass 
-            instanceVariableNames: aCollection 
-            package: packageName ].
+    | theClass |
+    
+    theClass := Smalltalk current at: aString.
+    
+   	theClass ifNotNil: [ 
+    	theClass superclass == aClass ifFalse: [
+    		^ self 
+        		migrateClassNamed: aString 
+           	 	superclass: aClass 
+           	 	instanceVariableNames: aCollection 
+            	package: packageName ] ].
 
 	^ self 
     	basicAddSubclassOf: aClass 
@@ -527,6 +531,7 @@ instanceVariableNamesFor: aString
 !
 
 migrateClass: aClass superclass: anotherClass
+	console log: aClass name.
 	self 
     	migrateClassNamed: aClass name
         superclass: anotherClass
@@ -537,6 +542,8 @@ migrateClass: aClass superclass: anotherClass
 migrateClassNamed: aString superclass: aClass instanceVariableNames: aCollection package: packageName
 	| oldClass newClass |
     
+    console log: '*** MIGRATING ', aString.
+    
     oldClass := Smalltalk current at: aString.
     
     "Rename the old class for existing instances"

部分文件因为文件数量过多而无法显示