Browse Source

_st modulized, boot.js is AMD module

Herbert Vojčík 11 years ago
parent
commit
4ce3f97af0
4 changed files with 868 additions and 857 deletions
  1. 852 854
      js/boot.js
  2. 3 0
      js/lib/_st.js
  3. 3 3
      js/lib/amber-in-browser.js
  4. 10 0
      js/lib/ensure-console.js

+ 852 - 854
js/boot.js

@@ -33,861 +33,859 @@
    |
    ==================================================================== */
 
-/* Make sure that console is defined */
-
-if(typeof console === "undefined") {
-	this.console = {
-		log: function() {},
-		warn: function() {},
-		info: function() {},
-		debug: function() {},
-		error: function() {}
-	};
-}
-
-/* Array extensions */
-
-Array.prototype.addElement = function(el) {
-	if(typeof el === 'undefined') { return; }
-	if(this.indexOf(el) == -1) {
-		this.push(el);
-	}
-};
-
-Array.prototype.removeElement = function(el) {
-	var i = this.indexOf(el);
-	if (i !== -1) { this.splice(i, 1); }
-};
-
-
-/* Smalltalk constructors definition */
-
-function SmalltalkObject() {}
-function SmalltalkBehavior() {}
-function SmalltalkClass() {}
-function SmalltalkMetaclass() {
-	this.meta = true;
-}
-function SmalltalkPackage() {}
-function SmalltalkMethod() {}
-function SmalltalkNil() {}
-
-function SmalltalkOrganizer() {
-}
-
-function SmalltalkPackageOrganizer() {
-	this.elements = [];
-}
-
-function SmalltalkClassOrganizer() {
-	this.elements = [];
-}
-
-function inherits(child, parent) {
-	child.prototype = Object.create(parent.prototype, {
-		constructor: { value: child,
-			enumerable: false, configurable: true, writable: true }
-	});
-}
-
-inherits(SmalltalkBehavior, SmalltalkObject);
-inherits(SmalltalkClass, SmalltalkBehavior);
-inherits(SmalltalkMetaclass, SmalltalkBehavior);
-inherits(SmalltalkNil, SmalltalkObject);
-inherits(SmalltalkMethod, SmalltalkObject);
-inherits(SmalltalkPackage, SmalltalkObject);
-inherits(SmalltalkOrganizer, SmalltalkObject);
-inherits(SmalltalkPackageOrganizer, SmalltalkOrganizer);
-inherits(SmalltalkClassOrganizer, SmalltalkOrganizer);
-
-
-function Smalltalk() {
-
-	var st = this;
-
-	/* This is the current call context object. While it is publicly available,
-		Use smalltalk.getThisContext() instead which will answer a safe copy of
-		the current context */
-
-	st.thisContext = undefined;
-
-	/* List of all reserved words in JavaScript. They may not be used as variables
-		in Smalltalk. */
-
-	// list of reserved JavaScript keywords as of
-	//   http://es5.github.com/#x7.6.1.1
-	// and
-	//   http://people.mozilla.org/~jorendorff/es6-draft.html#sec-7.6.1
-	st.reservedWords = ['break', 'case', 'catch', 'continue', 'debugger',
-		'default', 'delete', 'do', 'else', 'finally', 'for', 'function',
-		'if', 'in', 'instanceof', 'new', 'return', 'switch', 'this', 'throw',
-		'try', 'typeof', 'var', 'void', 'while', 'with',
-		// ES5: future use: http://es5.github.com/#x7.6.1.2
-		'class', 'const', 'enum', 'export', 'extends', 'import', 'super',
-		// ES5: future use in strict mode
-		'implements', 'interface', 'let', 'package', 'private', 'protected',
-		'public', 'static', 'yield'];
-
-	st.globalJsVariables = ['jQuery', 'window', 'document', 'process', 'global'];
-
-	var initialized = false;
-
-	/* Smalltalk classes */
-
-	var classes = [];
-	var wrappedClasses = [];
-
-	/* Method not implemented handlers */
-
-	var dnu = {
-		methods: [],
-		selectors: [],
-
-		get: function (string) {
-			var index = this.selectors.indexOf(string);
-			if(index !== -1) {
-				return this.methods[index];
-			}
-			this.selectors.push(string);
-			var selector = st.selector(string);
-			var method = {jsSelector: selector, fn: this.createHandler(selector)};
-			this.methods.push(method);
-			return method;
-		},
-
-		/* Dnu handler method */
-
-		createHandler: function (selector) {
-			var handler = function() {
-				var args = Array.prototype.slice.call(arguments);
-				return messageNotUnderstood(this, selector, args);
-			};
-
-			return handler;
-		}
-	};
-
-	/* Answer all method selectors based on dnu handlers */
-
-	st.allSelectors = function() {
-		return dnu.selectors;
-	};
-
-	/* Unique ID number generator */
-
-	var oid = 0;
-	st.nextId = function() {
-		oid += 1;
-		return oid;
-	};
-
-	/* We hold all Packages in a separate Object */
-
-	st.packages = {};
-
-	/* Smalltalk package creation. To add a Package, use smalltalk.addPackage() */
-
-	function pkg(spec) {
-		var that = new SmalltalkPackage();
-		that.pkgName = spec.pkgName;
-		that.organization = new SmalltalkPackageOrganizer();
-		that.properties = spec.properties || {};
-		return that;
-	}
-
-	/* Smalltalk class creation. A class is an instance of an automatically
-		created metaclass object. Newly created classes (not their metaclass) 
-		should be added to the smalltalk object, see smalltalk.addClass().
-		Superclass linking is *not* handled here, see smalltalk.init()  */
-
-	function klass(spec) {
-		spec = spec || {};
-		var meta = metaclass(spec);
-		var that = meta.instanceClass;
-		that.fn = spec.fn || function() {};
-		setupClass(that, spec);
-
-		that.className = spec.className;
-		that.wrapped   = spec.wrapped || false;
-		meta.className = spec.className + ' class';
-		if(spec.superclass) {
-			that.superclass = spec.superclass;
-			meta.superclass = spec.superclass.klass;
-		}
-		return that;
-	}
-
-	function metaclass(spec) {
-		spec = spec || {};
-		var that = new SmalltalkMetaclass();
-		inherits(
-			that.fn = function() {},
-			spec.superclass ? spec.superclass.klass.fn : SmalltalkClass
-		);
-		that.instanceClass = new that.fn();
-		setupClass(that);
-		return that;
-	}
-
-	function setupClass(klass, spec) {
-		spec = spec || {};
-		klass.iVarNames = spec.iVarNames || [];
-		klass.pkg = spec.pkg;
-
-		Object.defineProperty(klass, "toString", {
-			value: function() { return 'Smalltalk ' + this.className; },
-			enumerable:false, configurable: true, writable: false
-		});
-
-		klass.organization          = new SmalltalkClassOrganizer();
-		klass.organization.theClass = klass;
-
-		Object.defineProperty(klass, "methods", {
-			value: {},
-			enumerable: false, configurable: true, writable: true
-		});
-		wireKlass(klass);
-	}
-
-	/* Smalltalk method object. To add a method to a class,
-		use smalltalk.addMethod() */
-
-	st.method = function(spec) {
-		var that = new SmalltalkMethod();
-		that.selector          = spec.selector;
-		that.jsSelector        = spec.jsSelector;
-		that.args              = spec.args || {};
-		that.category          = spec.category;
-		that.source            = spec.source;
-		that.messageSends      = spec.messageSends || [];
-		that.referencedClasses = spec.referencedClasses || [];
-		that.fn                = spec.fn;
-		return that;
-	};
-
-	/* Initialize a class in its class hierarchy. Handle both classes and
-		metaclasses. */
-
-	st.init = function(klass) {
-		st.initClass(klass);
-		if(klass.klass && !klass.meta) {
-			st.initClass(klass.klass);
-		}
-	};
-
-	st.initClass = function(klass) {
-		if(klass.wrapped) {
-			klass.inheritedMethods = {};
-			copySuperclass(klass);
-		} else {
-			installSuperclass(klass);
-		}
-
-		if(klass === st.Object || klass.wrapped) {
-			installDnuHandlers(klass);
-		}
-	};
-
-	function wireKlass(klass) {
-		Object.defineProperty(klass.fn.prototype, "klass", {
-			value: klass,
-			enumerable: false, configurable: true, writable: true
-		});
-	}
-
-	function installSuperclass(klass) {
-		// only if the klass has not been initialized yet.
-		if(klass.fn.prototype._yourself) { return; }
-
-		if(klass.superclass && klass.superclass !== nil) {
-			inherits(klass.fn, klass.superclass.fn);
-			wireKlass(klass);
-			reinstallMethods(klass);
-		}
-	}
-
-	function copySuperclass(klass, superclass) {
-		for (superclass = superclass || klass.superclass;
-			superclass && superclass !== nil;
-			superclass = superclass.superclass) {
-			for (var keys = Object.keys(superclass.methods), i = 0; i < keys.length; i++) {
-				inheritMethodIfAbsent(superclass.methods[keys[i]], klass);
-			}
-		}
-	}
-
-	function installMethod(method, klass) {
-		Object.defineProperty(klass.fn.prototype, method.jsSelector, {
-			value: method.fn,
-			enumerable: false, configurable: true, writable: true
-		});
-	}
-
-	function inheritMethodIfAbsent(method, klass) {
-		var selector = method.selector;
-
-		if(klass.methods.hasOwnProperty(selector) || klass.inheritedMethods.hasOwnProperty(selector)) {
-			return;
-		}
-
-		installMethod(method, klass);
-		klass.inheritedMethods[method.selector] = true;
-	}
-
-	function reinstallMethods(klass) {
-		for(var keys = Object.keys(klass.methods), i=0; i<keys.length; i++) {
-			installMethod(klass.methods[keys[i]], klass);
-		}
-	}
-
-	function installDnuHandlers(klass) {
-		var m = dnu.methods;
-		for(var i=0; i<m.length; i++) {
-			installDnuHandlerIfAbsent(m[i], klass);
-		}
-	}
-
-	function installNewDnuHandler(newHandler) {
-		installDnuHandlerIfAbsent(newHandler, st.Object);
-		for(var i = 0; i < wrappedClasses.length; i++) {
-			installDnuHandlerIfAbsent(newHandler, wrappedClasses[i]);
-		}
-	}
-
-	function installDnuHandlerIfAbsent(handler, klass) {
-		var jsFunction = klass.fn.prototype[handler.jsSelector];
-		if(!jsFunction) {
-			installMethod(handler, klass);
-		}
-	}
-
-	/* Answer all registered Packages as Array */
-	// TODO: Remove this hack
-
-	st.packages.all = function() {
-		var packages = [];
-		for(var i in st.packages) {
-			if(!st.packages.hasOwnProperty(i) || typeof(st.packages[i]) === "function") continue;
-			packages.push(st.packages[i]);
-		}
-		return packages;
-	};
-
-	/* Answer all registered Smalltalk classes */
-	//TODO: remove the function and make smalltalk.classes an array
-
-	st.classes = function() {
-		return classes;
-	};
-
-	st.wrappedClasses = function() {
-		return wrappedClasses;
-	};
-
-	/* Answer the direct subclasses of klass. */
-
-	st.subclasses = function(klass) {
-		var subclasses = [];
-		var classes = st.classes();
-		for(var i=0; i < classes.length; i++) {
-			var c = classes[i];
-			if(c.fn) {
-				//Classes
-				if(c.superclass === klass) {
-					subclasses.push(c);
-				}
-				c = c.klass;
-				//Metaclasses
-				if(c && c.superclass === klass) {
-					subclasses.push(c);
-				}
-			}
-		}
-		return subclasses;
-	};
-
-	st.allSubclasses = function(klass) {
-		var result, subclasses;
-		result = subclasses = st.subclasses(klass);
-		subclasses.forEach(function(subclass) {
-			result.push.apply(result, st.allSubclasses(subclass));
-		});
-	
-		return result;
-	};
-
-
-	/* Create a new class wrapping a JavaScript constructor, and add it to the
-		global smalltalk object. Package is lazily created if it does not exist with given name. */
-
-	st.wrapClassName = function(className, pkgName, fn, superclass, wrapped) {
-		if(wrapped !== false) {
-			wrapped = true;
-		}
-		var pkg = st.addPackage(pkgName);
-		st[className] = klass({
-			className:  className,
-			superclass: superclass,
-			pkg:        pkg,
-			fn:         fn,
-			wrapped:    wrapped
-		});
-
-		classes.addElement(st[className]);
-		if(wrapped) {
-			wrappedClasses.addElement(st[className]);
-		}
-		pkg.organization.elements.addElement(st[className]);
-	};
-
-	/* Create an alias for an existing class */
-
-	st.alias = function(klass, alias) {
-		st[alias] = klass;
-	};
-
-	/* Add a package to the smalltalk.packages object, creating a new one if needed.
-		If pkgName is null or empty we return nil, which is an allowed package for a class.
-		If package already exists we still update the properties of it. */
-
-	st.addPackage = function(pkgName, properties) {
-		if(!pkgName) {return nil;}
-		if(!(st.packages[pkgName])) {
-			st.packages[pkgName] = pkg({
-				pkgName: pkgName,
-				properties: properties
-			});
-		} else {
-			if(properties) {
-				st.packages[pkgName].properties = properties;
-			}
-		}
-		return st.packages[pkgName];
-	};
-
-	/* Add a class to the smalltalk object, creating a new one if needed.
-		A Package is lazily created if it does not exist with given name. */
-
-	st.addClass = function(className, superclass, iVarNames, pkgName) {
-		var pkg = st.addPackage(pkgName);
-		if (superclass == nil) { superclass = null; }
-		if(st[className] && st[className].superclass == superclass) {
-			st[className].superclass = superclass;
-			st[className].iVarNames = iVarNames;
-			st[className].pkg = pkg || st[className].pkg;
-		} else {
-			if(st[className]) {
-				st.removeClass(st[className]);
-			}
-			st[className] = klass({
-				className: className,
-				superclass: superclass,
-				pkg: pkg,
-				iVarNames: iVarNames
-			});
-		}
-
-		classes.addElement(st[className]);
-		pkg.organization.elements.addElement(st[className]);
-	};
-
-	st.removeClass = function(klass) {
-		klass.pkg.organization.elements.removeElement(klass);
-		classes.removeElement(klass);
-		delete st[klass.className];
-	};
-
-	/* Add/remove a method to/from a class */
-
-	/* This is a temporary version of addMethod() for backward compatibility */
-	st.addMethod = function(method_exJsSelector, klass_exMethod, exKlass) {
-		if (typeof method_exJsSelector === "string") { //legacy
-			if (method_exJsSelector !== st.selector(klass_exMethod.selector)) {
-				console.log("DISCREPANCY: arg, in_method");
-				console.log(method_exJsSelector);
-				console.log(st.selector(klass_exMethod.selector));
-				klass_exMethod.jsSelector = method_exJsSelector;
-			}
-			return new_addMethod(klass_exMethod, exKlass);
-		}
-	
-		return new_addMethod(method_exJsSelector, klass_exMethod);
-	};
-	
-	// later, st.addMethod can be this:
-	function new_addMethod(method, klass) {
-		if (!(method.jsSelector)) {
-			method.jsSelector = st.selector(method.selector);
-		}
-		installMethod(method, klass);
-		klass.methods[method.selector] = method;
-		method.methodClass = klass;
-
-		// During the bootstrap, #addCompiledMethod is not used.
-		// Therefore we populate the organizer here too
-		klass.organization.elements.addElement(method.category);
-	
-		// If already initialized (else it will be done later anyway),
-		// re-initialize all subclasses to ensure the new method
-		// propagation (for wrapped classes, not using the prototype
-		// chain.
-		if(initialized) {
-			st.allSubclasses(klass).forEach(function(subclass) {
-				st.initClass(subclass);
-			});
-		}
-	
-		for(var i=0; i<method.messageSends.length; i++) {
-			var dnuHandler = dnu.get(method.messageSends[i]);
-			if(initialized) {
-				installNewDnuHandler(dnuHandler);
-			}
-		}
-	}
-
-	st.removeMethod = function(method) {
-		var klass = method.methodClass;
-
-		delete klass.fn.prototype[st.selector(method.selector)];
-		delete klass.methods[method.selector];
-
-		// Do *not* delete protocols from here.
-		// This is handled by #removeCompiledMethod
-	};
-
-	/* Handles unhandled errors during message sends */
-	// simply send the message and handle #dnu:
-
-	st.send = function(receiver, selector, args, klass) {
-		var method;
-		if(receiver === null) {
-			receiver = nil;
-		}
-		method = klass ? klass.fn.prototype[selector] : receiver.klass && receiver[selector];
-		if(method) {
-			return method.apply(receiver, args);
-		} else {
-			return messageNotUnderstood(receiver, selector, args);
-		}
-	};
-
-	st.withContext = function(worker, setup) {
-		if(st.thisContext) {
-			st.thisContext.pc++;
-			return inContext(worker, setup);
-		} else {
-			try {
-				return inContext(worker, setup);
-			} catch(error) {
-				if(error.smalltalkError) {
-					handleError(error);
-				} else {
-					var errorWrapper = st.JavaScriptException._on_(error);
-					try {errorWrapper._signal();} catch(ex) {}
-					errorWrapper._context_(st.getThisContext());
-					handleError(errorWrapper);
-				}
-				// Reset the context stack in any case
-				st.thisContext = undefined;
-				// Throw the exception anyway, as we want to stop
-				// the execution to avoid infinite loops
-				// Update: do not throw the exception. It's really annoying.
-				// throw error;
-			}
-		}
-	};
-
-	function inContext(worker, setup) {
-		var context = pushContext(setup);
-		var result = worker(context);
-		popContext(context);
-		return result;
-	}
-
-	/* Handles Smalltalk errors. Triggers the registered ErrorHandler
-		(See the Smalltalk class ErrorHandler and its subclasses */
-
-	function handleError(error) {
-		st.ErrorHandler._current()._handleError_(error);
-	}
-
-	/* Handles #dnu: *and* JavaScript method calls.
-		if the receiver has no klass, we consider it a JS object (outside of the
-		Amber system). Else assume that the receiver understands #doesNotUnderstand: */
-
-	function messageNotUnderstood(receiver, selector, args) {
-		/* Handles JS method calls. */
-		if(receiver.klass === undefined || receiver.allowJavaScriptCalls) {
-			return callJavaScriptMethod(receiver, selector, args);
-		}
-
-		/* Handles not understood messages. Also see the Amber counter-part
-			Object>>doesNotUnderstand: */
-
-		return receiver._doesNotUnderstand_(
-			st.Message._new()
-				._selector_(st.convertSelector(selector))
-				._arguments_(args)
-		);
-	}
-
-	/* Call a method of a JS object, or answer a property if it exists.
-		Else try wrapping a JSObjectProxy around the receiver.
-
-		If the object property is a function, then call it, except if it starts with
-		an uppercase character (we probably want to answer the function itself in this
-		case and send it #new from Amber).
-
-		Converts keyword-based selectors by using the first
-		keyword only, but keeping all message arguments.
-		
-		Example:
-		"self do: aBlock with: anObject" -> "self.do(aBlock, anObject)" */
-
-	function callJavaScriptMethod(receiver, selector, args) {
-		var jsSelector = selector._asJavaScriptSelector();
-		var jsProperty = receiver[jsSelector];
-		if(typeof jsProperty === "function" && !/^[A-Z]/.test(jsSelector)) {
-			return jsProperty.apply(receiver, args);
-		} else if(jsProperty !== undefined) {
-			if(args[0]) {
-				receiver[jsSelector] = args[0];
-				return nil;
-			} else {
-				return jsProperty;
-			}
-		}
-
-		return st.send(st.JSObjectProxy._on_(receiver), selector, args);
-	}
-
-	/* Handle thisContext pseudo variable */
-
-	st.getThisContext = function() {
-		if(st.thisContext) {
-			st.thisContext.init();
-			return st.thisContext;
-		} else {
-			return nil;
-		}
-	};
-
-	function pushContext(setup) {
-		st.thisContext = new SmalltalkMethodContext(smalltalk.thisContext, setup);
-		return st.thisContext;
-	}
-
-	function popContext(context) {
-		st.thisContext = context.homeContext;
-	}
-
-	/* Convert a Smalltalk selector into a JS selector */
-
-	st.selector = function(string) {
-		var selector = '_' + string;
-		selector = selector.replace(/:/g, '_');
-		selector = selector.replace(/[\&]/g, '_and');
-		selector = selector.replace(/[\|]/g, '_or');
-		selector = selector.replace(/[+]/g, '_plus');
-		selector = selector.replace(/-/g, '_minus');
-		selector = selector.replace(/[*]/g ,'_star');
-		selector = selector.replace(/[\/]/g ,'_slash');
-		selector = selector.replace(/[\\]/g ,'_backslash');
-		selector = selector.replace(/[\~]/g ,'_tild');
-		selector = selector.replace(/>/g ,'_gt');
-		selector = selector.replace(/</g ,'_lt');
-		selector = selector.replace(/=/g ,'_eq');
-		selector = selector.replace(/,/g ,'_comma');
-		selector = selector.replace(/[@]/g ,'_at');
-		return selector;
-	};
-
-	/* Convert a string to a valid smalltalk selector.
-		if you modify the following functions, also change String>>asSelector
-		accordingly */
-
-	st.convertSelector = function(selector) {
-		if(selector.match(/__/)) {
-			return convertBinarySelector(selector);
-		} else {
-			return convertKeywordSelector(selector);
-		}
-	};
-
-	function convertKeywordSelector(selector) {
-		return selector.replace(/^_/, '').replace(/_/g, ':');
-	}
-
-	function convertBinarySelector(selector) {
-		return selector
-			.replace(/^_/, '')
-			.replace(/_and/g, '&')
-			.replace(/_or/g, '|')
-			.replace(/_plus/g, '+')
-			.replace(/_minus/g, '-')
-			.replace(/_star/g, '*')
-			.replace(/_slash/g, '/')
-			.replace(/_backslash/g, '\\')
-			.replace(/_tild/g, '~')
-			.replace(/_gt/g, '>')
-			.replace(/_lt/g, '<')
-			.replace(/_eq/g, '=')
-			.replace(/_comma/g, ',')
-			.replace(/_at/g, '@');
-	}
-
-	/* Converts a JavaScript object to valid Smalltalk Object */
-	st.readJSObject = function(js) {
-		var object = js;
-		var readObject = (js.constructor === Object);
-		var readArray = (js.constructor === Array);
-
-		if(readObject) {
-			object = st.Dictionary._new();
-		}
-		for(var i in js) {
-			if(readObject) {
-				object._at_put_(i, st.readJSObject(js[i]));
-			}
-			if(readArray) {
-				object[i] = st.readJSObject(js[i]);
-			}
-		}
-		return object;
-	};
-
-	/* Boolean assertion */
-	st.assert = function(shouldBeBoolean) {
-		if ((undefined !== shouldBeBoolean) && (shouldBeBoolean.klass === smalltalk.Boolean)) {
-			return (shouldBeBoolean == true);
-		} else {
-			smalltalk.NonBooleanReceiver._new()._object_(shouldBeBoolean)._signal();
-		}
+define([ 'ensure-console', 'es5-shim-2.0.2/es5-shim.min', 'es5-shim-2.0.2/es5-sham.min' ], function () {
+    /* Array extensions */
+
+    Array.prototype.addElement = function(el) {
+        if(typeof el === 'undefined') { return; }
+        if(this.indexOf(el) == -1) {
+            this.push(el);
+        }
     };
 
-	/* Backward compatibility with Amber 0.9.1 */
-	st.symbolFor = function(aString) { return aString; };
-	
-	/* Smalltalk initialization. Called on page load */
-	
-	st.initialize = function() {
-		if(initialized) { return; }
-
-		classes.forEach(function(klass) {
-			st.init(klass);
-		});
-		classes.forEach(function(klass) {
-			klass._initialize();
-		});
-	
-		initialized = true;
-	};
-}
-
-inherits(Smalltalk, SmalltalkObject);
-
-function SmalltalkMethodContext(home, setup) {
-	this.homeContext = home;
-	this.setup       = setup || function() {};
-	this.pc          = 0;
-}
+    Array.prototype.removeElement = function(el) {
+        var i = this.indexOf(el);
+        if (i !== -1) { this.splice(i, 1); }
+    };
+
+
+    /* Smalltalk constructors definition */
+
+    function SmalltalkObject() {}
+    function SmalltalkBehavior() {}
+    function SmalltalkClass() {}
+    function SmalltalkMetaclass() {
+        this.meta = true;
+    }
+    function SmalltalkPackage() {}
+    function SmalltalkMethod() {}
+    function SmalltalkNil() {}
+
+    function SmalltalkOrganizer() {
+    }
+
+    function SmalltalkPackageOrganizer() {
+        this.elements = [];
+    }
+
+    function SmalltalkClassOrganizer() {
+        this.elements = [];
+    }
+
+    function inherits(child, parent) {
+        child.prototype = Object.create(parent.prototype, {
+            constructor: { value: child,
+                enumerable: false, configurable: true, writable: true }
+        });
+    }
+
+    inherits(SmalltalkBehavior, SmalltalkObject);
+    inherits(SmalltalkClass, SmalltalkBehavior);
+    inherits(SmalltalkMetaclass, SmalltalkBehavior);
+    inherits(SmalltalkNil, SmalltalkObject);
+    inherits(SmalltalkMethod, SmalltalkObject);
+    inherits(SmalltalkPackage, SmalltalkObject);
+    inherits(SmalltalkOrganizer, SmalltalkObject);
+    inherits(SmalltalkPackageOrganizer, SmalltalkOrganizer);
+    inherits(SmalltalkClassOrganizer, SmalltalkOrganizer);
+
+
+    function Smalltalk() {
+
+        var st = this;
+
+        /* This is the current call context object. While it is publicly available,
+         Use smalltalk.getThisContext() instead which will answer a safe copy of
+         the current context */
+
+        st.thisContext = undefined;
+
+        /* List of all reserved words in JavaScript. They may not be used as variables
+         in Smalltalk. */
+
+        // list of reserved JavaScript keywords as of
+        //   http://es5.github.com/#x7.6.1.1
+        // and
+        //   http://people.mozilla.org/~jorendorff/es6-draft.html#sec-7.6.1
+        st.reservedWords = ['break', 'case', 'catch', 'continue', 'debugger',
+            'default', 'delete', 'do', 'else', 'finally', 'for', 'function',
+            'if', 'in', 'instanceof', 'new', 'return', 'switch', 'this', 'throw',
+            'try', 'typeof', 'var', 'void', 'while', 'with',
+            // ES5: future use: http://es5.github.com/#x7.6.1.2
+            'class', 'const', 'enum', 'export', 'extends', 'import', 'super',
+            // ES5: future use in strict mode
+            'implements', 'interface', 'let', 'package', 'private', 'protected',
+            'public', 'static', 'yield'];
+
+        st.globalJsVariables = ['jQuery', 'window', 'document', 'process', 'global'];
+
+        var initialized = false;
+
+        /* Smalltalk classes */
+
+        var classes = [];
+        var wrappedClasses = [];
+
+        /* Method not implemented handlers */
+
+        var dnu = {
+            methods: [],
+            selectors: [],
+
+            get: function (string) {
+                var index = this.selectors.indexOf(string);
+                if(index !== -1) {
+                    return this.methods[index];
+                }
+                this.selectors.push(string);
+                var selector = st.selector(string);
+                var method = {jsSelector: selector, fn: this.createHandler(selector)};
+                this.methods.push(method);
+                return method;
+            },
+
+            /* Dnu handler method */
+
+            createHandler: function (selector) {
+                var handler = function() {
+                    var args = Array.prototype.slice.call(arguments);
+                    return messageNotUnderstood(this, selector, args);
+                };
+
+                return handler;
+            }
+        };
+
+        /* Answer all method selectors based on dnu handlers */
+
+        st.allSelectors = function() {
+            return dnu.selectors;
+        };
+
+        /* Unique ID number generator */
+
+        var oid = 0;
+        st.nextId = function() {
+            oid += 1;
+            return oid;
+        };
+
+        /* We hold all Packages in a separate Object */
+
+        st.packages = {};
+
+        /* Smalltalk package creation. To add a Package, use smalltalk.addPackage() */
+
+        function pkg(spec) {
+            var that = new SmalltalkPackage();
+            that.pkgName = spec.pkgName;
+            that.organization = new SmalltalkPackageOrganizer();
+            that.properties = spec.properties || {};
+            return that;
+        }
+
+        /* Smalltalk class creation. A class is an instance of an automatically
+         created metaclass object. Newly created classes (not their metaclass)
+         should be added to the smalltalk object, see smalltalk.addClass().
+         Superclass linking is *not* handled here, see smalltalk.init()  */
+
+        function klass(spec) {
+            spec = spec || {};
+            var meta = metaclass(spec);
+            var that = meta.instanceClass;
+            that.fn = spec.fn || function() {};
+            setupClass(that, spec);
+
+            that.className = spec.className;
+            that.wrapped   = spec.wrapped || false;
+            meta.className = spec.className + ' class';
+            if(spec.superclass) {
+                that.superclass = spec.superclass;
+                meta.superclass = spec.superclass.klass;
+            }
+            return that;
+        }
+
+        function metaclass(spec) {
+            spec = spec || {};
+            var that = new SmalltalkMetaclass();
+            inherits(
+                that.fn = function() {},
+                spec.superclass ? spec.superclass.klass.fn : SmalltalkClass
+            );
+            that.instanceClass = new that.fn();
+            setupClass(that);
+            return that;
+        }
+
+        function setupClass(klass, spec) {
+            spec = spec || {};
+            klass.iVarNames = spec.iVarNames || [];
+            klass.pkg = spec.pkg;
+
+            Object.defineProperty(klass, "toString", {
+                value: function() { return 'Smalltalk ' + this.className; },
+                enumerable:false, configurable: true, writable: false
+            });
+
+            klass.organization          = new SmalltalkClassOrganizer();
+            klass.organization.theClass = klass;
+
+            Object.defineProperty(klass, "methods", {
+                value: {},
+                enumerable: false, configurable: true, writable: true
+            });
+            wireKlass(klass);
+        }
+
+        /* Smalltalk method object. To add a method to a class,
+         use smalltalk.addMethod() */
+
+        st.method = function(spec) {
+            var that = new SmalltalkMethod();
+            that.selector          = spec.selector;
+            that.jsSelector        = spec.jsSelector;
+            that.args              = spec.args || {};
+            that.category          = spec.category;
+            that.source            = spec.source;
+            that.messageSends      = spec.messageSends || [];
+            that.referencedClasses = spec.referencedClasses || [];
+            that.fn                = spec.fn;
+            return that;
+        };
+
+        /* Initialize a class in its class hierarchy. Handle both classes and
+         metaclasses. */
+
+        st.init = function(klass) {
+            st.initClass(klass);
+            if(klass.klass && !klass.meta) {
+                st.initClass(klass.klass);
+            }
+        };
+
+        st.initClass = function(klass) {
+            if(klass.wrapped) {
+                klass.inheritedMethods = {};
+                copySuperclass(klass);
+            } else {
+                installSuperclass(klass);
+            }
+
+            if(klass === st.Object || klass.wrapped) {
+                installDnuHandlers(klass);
+            }
+        };
+
+        function wireKlass(klass) {
+            Object.defineProperty(klass.fn.prototype, "klass", {
+                value: klass,
+                enumerable: false, configurable: true, writable: true
+            });
+        }
+
+        function installSuperclass(klass) {
+            // only if the klass has not been initialized yet.
+            if(klass.fn.prototype._yourself) { return; }
+
+            if(klass.superclass && klass.superclass !== nil) {
+                inherits(klass.fn, klass.superclass.fn);
+                wireKlass(klass);
+                reinstallMethods(klass);
+            }
+        }
+
+        function copySuperclass(klass, superclass) {
+            for (superclass = superclass || klass.superclass;
+                 superclass && superclass !== nil;
+                 superclass = superclass.superclass) {
+                for (var keys = Object.keys(superclass.methods), i = 0; i < keys.length; i++) {
+                    inheritMethodIfAbsent(superclass.methods[keys[i]], klass);
+                }
+            }
+        }
+
+        function installMethod(method, klass) {
+            Object.defineProperty(klass.fn.prototype, method.jsSelector, {
+                value: method.fn,
+                enumerable: false, configurable: true, writable: true
+            });
+        }
+
+        function inheritMethodIfAbsent(method, klass) {
+            var selector = method.selector;
+
+            if(klass.methods.hasOwnProperty(selector) || klass.inheritedMethods.hasOwnProperty(selector)) {
+                return;
+            }
+
+            installMethod(method, klass);
+            klass.inheritedMethods[method.selector] = true;
+        }
+
+        function reinstallMethods(klass) {
+            for(var keys = Object.keys(klass.methods), i=0; i<keys.length; i++) {
+                installMethod(klass.methods[keys[i]], klass);
+            }
+        }
+
+        function installDnuHandlers(klass) {
+            var m = dnu.methods;
+            for(var i=0; i<m.length; i++) {
+                installDnuHandlerIfAbsent(m[i], klass);
+            }
+        }
+
+        function installNewDnuHandler(newHandler) {
+            installDnuHandlerIfAbsent(newHandler, st.Object);
+            for(var i = 0; i < wrappedClasses.length; i++) {
+                installDnuHandlerIfAbsent(newHandler, wrappedClasses[i]);
+            }
+        }
+
+        function installDnuHandlerIfAbsent(handler, klass) {
+            var jsFunction = klass.fn.prototype[handler.jsSelector];
+            if(!jsFunction) {
+                installMethod(handler, klass);
+            }
+        }
+
+        /* Answer all registered Packages as Array */
+        // TODO: Remove this hack
+
+        st.packages.all = function() {
+            var packages = [];
+            for(var i in st.packages) {
+                if(!st.packages.hasOwnProperty(i) || typeof(st.packages[i]) === "function") continue;
+                packages.push(st.packages[i]);
+            }
+            return packages;
+        };
+
+        /* Answer all registered Smalltalk classes */
+        //TODO: remove the function and make smalltalk.classes an array
+
+        st.classes = function() {
+            return classes;
+        };
+
+        st.wrappedClasses = function() {
+            return wrappedClasses;
+        };
+
+        /* Answer the direct subclasses of klass. */
+
+        st.subclasses = function(klass) {
+            var subclasses = [];
+            var classes = st.classes();
+            for(var i=0; i < classes.length; i++) {
+                var c = classes[i];
+                if(c.fn) {
+                    //Classes
+                    if(c.superclass === klass) {
+                        subclasses.push(c);
+                    }
+                    c = c.klass;
+                    //Metaclasses
+                    if(c && c.superclass === klass) {
+                        subclasses.push(c);
+                    }
+                }
+            }
+            return subclasses;
+        };
+
+        st.allSubclasses = function(klass) {
+            var result, subclasses;
+            result = subclasses = st.subclasses(klass);
+            subclasses.forEach(function(subclass) {
+                result.push.apply(result, st.allSubclasses(subclass));
+            });
+
+            return result;
+        };
+
+
+        /* Create a new class wrapping a JavaScript constructor, and add it to the
+         global smalltalk object. Package is lazily created if it does not exist with given name. */
+
+        st.wrapClassName = function(className, pkgName, fn, superclass, wrapped) {
+            if(wrapped !== false) {
+                wrapped = true;
+            }
+            var pkg = st.addPackage(pkgName);
+            st[className] = klass({
+                className:  className,
+                superclass: superclass,
+                pkg:        pkg,
+                fn:         fn,
+                wrapped:    wrapped
+            });
+
+            classes.addElement(st[className]);
+            if(wrapped) {
+                wrappedClasses.addElement(st[className]);
+            }
+            pkg.organization.elements.addElement(st[className]);
+        };
+
+        /* Create an alias for an existing class */
+
+        st.alias = function(klass, alias) {
+            st[alias] = klass;
+        };
+
+        /* Add a package to the smalltalk.packages object, creating a new one if needed.
+         If pkgName is null or empty we return nil, which is an allowed package for a class.
+         If package already exists we still update the properties of it. */
+
+        st.addPackage = function(pkgName, properties) {
+            if(!pkgName) {return nil;}
+            if(!(st.packages[pkgName])) {
+                st.packages[pkgName] = pkg({
+                    pkgName: pkgName,
+                    properties: properties
+                });
+            } else {
+                if(properties) {
+                    st.packages[pkgName].properties = properties;
+                }
+            }
+            return st.packages[pkgName];
+        };
+
+        /* Add a class to the smalltalk object, creating a new one if needed.
+         A Package is lazily created if it does not exist with given name. */
+
+        st.addClass = function(className, superclass, iVarNames, pkgName) {
+            var pkg = st.addPackage(pkgName);
+            if (superclass == nil) { superclass = null; }
+            if(st[className] && st[className].superclass == superclass) {
+                st[className].superclass = superclass;
+                st[className].iVarNames = iVarNames;
+                st[className].pkg = pkg || st[className].pkg;
+            } else {
+                if(st[className]) {
+                    st.removeClass(st[className]);
+                }
+                st[className] = klass({
+                    className: className,
+                    superclass: superclass,
+                    pkg: pkg,
+                    iVarNames: iVarNames
+                });
+            }
+
+            classes.addElement(st[className]);
+            pkg.organization.elements.addElement(st[className]);
+        };
+
+        st.removeClass = function(klass) {
+            klass.pkg.organization.elements.removeElement(klass);
+            classes.removeElement(klass);
+            delete st[klass.className];
+        };
+
+        /* Add/remove a method to/from a class */
+
+        /* This is a temporary version of addMethod() for backward compatibility */
+        st.addMethod = function(method_exJsSelector, klass_exMethod, exKlass) {
+            if (typeof method_exJsSelector === "string") { //legacy
+                if (method_exJsSelector !== st.selector(klass_exMethod.selector)) {
+                    console.log("DISCREPANCY: arg, in_method");
+                    console.log(method_exJsSelector);
+                    console.log(st.selector(klass_exMethod.selector));
+                    klass_exMethod.jsSelector = method_exJsSelector;
+                }
+                return new_addMethod(klass_exMethod, exKlass);
+            }
+
+            return new_addMethod(method_exJsSelector, klass_exMethod);
+        };
+
+        // later, st.addMethod can be this:
+        function new_addMethod(method, klass) {
+            if (!(method.jsSelector)) {
+                method.jsSelector = st.selector(method.selector);
+            }
+            installMethod(method, klass);
+            klass.methods[method.selector] = method;
+            method.methodClass = klass;
+
+            // During the bootstrap, #addCompiledMethod is not used.
+            // Therefore we populate the organizer here too
+            klass.organization.elements.addElement(method.category);
+
+            // If already initialized (else it will be done later anyway),
+            // re-initialize all subclasses to ensure the new method
+            // propagation (for wrapped classes, not using the prototype
+            // chain.
+            if(initialized) {
+                st.allSubclasses(klass).forEach(function(subclass) {
+                    st.initClass(subclass);
+                });
+            }
+
+            for(var i=0; i<method.messageSends.length; i++) {
+                var dnuHandler = dnu.get(method.messageSends[i]);
+                if(initialized) {
+                    installNewDnuHandler(dnuHandler);
+                }
+            }
+        }
+
+        st.removeMethod = function(method) {
+            var klass = method.methodClass;
+
+            delete klass.fn.prototype[st.selector(method.selector)];
+            delete klass.methods[method.selector];
+
+            // Do *not* delete protocols from here.
+            // This is handled by #removeCompiledMethod
+        };
+
+        /* Handles unhandled errors during message sends */
+        // simply send the message and handle #dnu:
+
+        st.send = function(receiver, selector, args, klass) {
+            var method;
+            if(receiver === null) {
+                receiver = nil;
+            }
+            method = klass ? klass.fn.prototype[selector] : receiver.klass && receiver[selector];
+            if(method) {
+                return method.apply(receiver, args);
+            } else {
+                return messageNotUnderstood(receiver, selector, args);
+            }
+        };
+
+        st.withContext = function(worker, setup) {
+            if(st.thisContext) {
+                st.thisContext.pc++;
+                return inContext(worker, setup);
+            } else {
+                try {
+                    return inContext(worker, setup);
+                } catch(error) {
+                    if(error.smalltalkError) {
+                        handleError(error);
+                    } else {
+                        var errorWrapper = st.JavaScriptException._on_(error);
+                        try {errorWrapper._signal();} catch(ex) {}
+                        errorWrapper._context_(st.getThisContext());
+                        handleError(errorWrapper);
+                    }
+                    // Reset the context stack in any case
+                    st.thisContext = undefined;
+                    // Throw the exception anyway, as we want to stop
+                    // the execution to avoid infinite loops
+                    // Update: do not throw the exception. It's really annoying.
+                    // throw error;
+                }
+            }
+        };
+
+        function inContext(worker, setup) {
+            var context = pushContext(setup);
+            var result = worker(context);
+            popContext(context);
+            return result;
+        }
+
+        /* Handles Smalltalk errors. Triggers the registered ErrorHandler
+         (See the Smalltalk class ErrorHandler and its subclasses */
+
+        function handleError(error) {
+            st.ErrorHandler._current()._handleError_(error);
+        }
+
+        /* Handles #dnu: *and* JavaScript method calls.
+         if the receiver has no klass, we consider it a JS object (outside of the
+         Amber system). Else assume that the receiver understands #doesNotUnderstand: */
+
+        function messageNotUnderstood(receiver, selector, args) {
+            /* Handles JS method calls. */
+            if(receiver.klass === undefined || receiver.allowJavaScriptCalls) {
+                return callJavaScriptMethod(receiver, selector, args);
+            }
+
+            /* Handles not understood messages. Also see the Amber counter-part
+             Object>>doesNotUnderstand: */
+
+            return receiver._doesNotUnderstand_(
+                st.Message._new()
+                    ._selector_(st.convertSelector(selector))
+                    ._arguments_(args)
+            );
+        }
+
+        /* Call a method of a JS object, or answer a property if it exists.
+         Else try wrapping a JSObjectProxy around the receiver.
+
+         If the object property is a function, then call it, except if it starts with
+         an uppercase character (we probably want to answer the function itself in this
+         case and send it #new from Amber).
+
+         Converts keyword-based selectors by using the first
+         keyword only, but keeping all message arguments.
+
+         Example:
+         "self do: aBlock with: anObject" -> "self.do(aBlock, anObject)" */
+
+        function callJavaScriptMethod(receiver, selector, args) {
+            var jsSelector = selector._asJavaScriptSelector();
+            var jsProperty = receiver[jsSelector];
+            if(typeof jsProperty === "function" && !/^[A-Z]/.test(jsSelector)) {
+                return jsProperty.apply(receiver, args);
+            } else if(jsProperty !== undefined) {
+                if(args[0]) {
+                    receiver[jsSelector] = args[0];
+                    return nil;
+                } else {
+                    return jsProperty;
+                }
+            }
+
+            return st.send(st.JSObjectProxy._on_(receiver), selector, args);
+        }
+
+        /* Handle thisContext pseudo variable */
+
+        st.getThisContext = function() {
+            if(st.thisContext) {
+                st.thisContext.init();
+                return st.thisContext;
+            } else {
+                return nil;
+            }
+        };
+
+        function pushContext(setup) {
+            st.thisContext = new SmalltalkMethodContext(smalltalk.thisContext, setup);
+            return st.thisContext;
+        }
+
+        function popContext(context) {
+            st.thisContext = context.homeContext;
+        }
+
+        /* Convert a Smalltalk selector into a JS selector */
+
+        st.selector = function(string) {
+            var selector = '_' + string;
+            selector = selector.replace(/:/g, '_');
+            selector = selector.replace(/[\&]/g, '_and');
+            selector = selector.replace(/[\|]/g, '_or');
+            selector = selector.replace(/[+]/g, '_plus');
+            selector = selector.replace(/-/g, '_minus');
+            selector = selector.replace(/[*]/g ,'_star');
+            selector = selector.replace(/[\/]/g ,'_slash');
+            selector = selector.replace(/[\\]/g ,'_backslash');
+            selector = selector.replace(/[\~]/g ,'_tild');
+            selector = selector.replace(/>/g ,'_gt');
+            selector = selector.replace(/</g ,'_lt');
+            selector = selector.replace(/=/g ,'_eq');
+            selector = selector.replace(/,/g ,'_comma');
+            selector = selector.replace(/[@]/g ,'_at');
+            return selector;
+        };
+
+        /* Convert a string to a valid smalltalk selector.
+         if you modify the following functions, also change String>>asSelector
+         accordingly */
+
+        st.convertSelector = function(selector) {
+            if(selector.match(/__/)) {
+                return convertBinarySelector(selector);
+            } else {
+                return convertKeywordSelector(selector);
+            }
+        };
+
+        function convertKeywordSelector(selector) {
+            return selector.replace(/^_/, '').replace(/_/g, ':');
+        }
+
+        function convertBinarySelector(selector) {
+            return selector
+                .replace(/^_/, '')
+                .replace(/_and/g, '&')
+                .replace(/_or/g, '|')
+                .replace(/_plus/g, '+')
+                .replace(/_minus/g, '-')
+                .replace(/_star/g, '*')
+                .replace(/_slash/g, '/')
+                .replace(/_backslash/g, '\\')
+                .replace(/_tild/g, '~')
+                .replace(/_gt/g, '>')
+                .replace(/_lt/g, '<')
+                .replace(/_eq/g, '=')
+                .replace(/_comma/g, ',')
+                .replace(/_at/g, '@');
+        }
+
+        /* Converts a JavaScript object to valid Smalltalk Object */
+        st.readJSObject = function(js) {
+            var object = js;
+            var readObject = (js.constructor === Object);
+            var readArray = (js.constructor === Array);
+
+            if(readObject) {
+                object = st.Dictionary._new();
+            }
+            for(var i in js) {
+                if(readObject) {
+                    object._at_put_(i, st.readJSObject(js[i]));
+                }
+                if(readArray) {
+                    object[i] = st.readJSObject(js[i]);
+                }
+            }
+            return object;
+        };
+
+        /* Boolean assertion */
+        st.assert = function(shouldBeBoolean) {
+            if ((undefined !== shouldBeBoolean) && (shouldBeBoolean.klass === smalltalk.Boolean)) {
+                return (shouldBeBoolean == true);
+            } else {
+                smalltalk.NonBooleanReceiver._new()._object_(shouldBeBoolean)._signal();
+            }
+        };
+
+        /* Backward compatibility with Amber 0.9.1 */
+        st.symbolFor = function(aString) { return aString; };
+
+        /* Smalltalk initialization. Called on page load */
+
+        st.initialize = function() {
+            if(initialized) { return; }
+
+            classes.forEach(function(klass) {
+                st.init(klass);
+            });
+            classes.forEach(function(klass) {
+                klass._initialize();
+            });
+
+            initialized = true;
+        };
+    }
+
+    inherits(Smalltalk, SmalltalkObject);
+
+    function SmalltalkMethodContext(home, setup) {
+        this.homeContext = home;
+        this.setup       = setup || function() {};
+        this.pc          = 0;
+    }
 
 // Fallbacks
-SmalltalkMethodContext.prototype.locals = {};
-SmalltalkMethodContext.prototype.receiver = null;
-SmalltalkMethodContext.prototype.selector = null;
-SmalltalkMethodContext.prototype.lookupClass = null;
-
-inherits(SmalltalkMethodContext, SmalltalkObject);
-
-SmalltalkMethodContext.prototype.fill = function(receiver, selector, locals, lookupClass) {
-	this.receiver    = receiver;
-	this.selector    = selector;
-	this.locals      = locals || {};
-	this.lookupClass = lookupClass;
-};
-
-SmalltalkMethodContext.prototype.fillBlock = function(locals, ctx) {
-	this.locals        = locals || {};
-	this.outerContext  = ctx;
-};
-
-SmalltalkMethodContext.prototype.init = function() {
-	var home = this.homeContext;
-	if(home) {
-		home = home.init();
-	}
-
-    this.setup(this);
-};
-
-SmalltalkMethodContext.prototype.method = function() {
-	var method;
-	var lookup = this.lookupClass || this.receiver.klass;
-	while(!method && lookup) {
-		method = lookup.methods[smalltalk.convertSelector(this.selector)];
-		lookup = lookup.superclass;
-	}
-	return method;
-};
-
-/* Global Smalltalk objects. */
-
-var nil = new SmalltalkNil();
-var smalltalk = new Smalltalk();
-
-if(this.jQuery) {
-	this.jQuery.allowJavaScriptCalls = true;
-}
-
-/*
- * Answer the smalltalk representation of o.
- * Used in message sends
- */
-
-var _st = function(o) {
-	if(o == null) {return nil;}
-	if(o.klass) {return o;}
-	return smalltalk.JSObjectProxy._on_(o);
-}; 
-
-
-/***************************************** BOOTSTRAP ******************************************/
-
-smalltalk.wrapClassName("Object", "Kernel-Objects", SmalltalkObject, undefined, false);
-smalltalk.wrapClassName("Behavior", "Kernel-Classes", SmalltalkBehavior, smalltalk.Object, false);
-smalltalk.wrapClassName("Metaclass", "Kernel-Classes", SmalltalkMetaclass, smalltalk.Behavior, false);
-smalltalk.wrapClassName("Class", "Kernel-Classes", SmalltalkClass, smalltalk.Behavior, false);
-
-smalltalk.Object.klass.superclass = smalltalk.Class;
-
-
-smalltalk.wrapClassName("Smalltalk", "Kernel-Objects", Smalltalk, smalltalk.Object, false);
-smalltalk.wrapClassName("Package", "Kernel-Objects", SmalltalkPackage, smalltalk.Object, false);
-smalltalk.wrapClassName("CompiledMethod", "Kernel-Methods", SmalltalkMethod, smalltalk.Object, false);
-smalltalk.wrapClassName("Organizer", "Kernel-Objects", SmalltalkOrganizer, smalltalk.Object, false);
-smalltalk.wrapClassName("PackageOrganizer", "Kernel-Objects", SmalltalkPackageOrganizer, smalltalk.Organizer, false);
-smalltalk.wrapClassName("ClassOrganizer", "Kernel-Objects", SmalltalkClassOrganizer, smalltalk.Organizer, false);
-
-
-smalltalk.wrapClassName("Number", "Kernel-Objects", Number, smalltalk.Object);
-smalltalk.wrapClassName("BlockClosure", "Kernel-Methods", Function, smalltalk.Object);
-smalltalk.wrapClassName("Boolean", "Kernel-Objects", Boolean, smalltalk.Object);
-smalltalk.wrapClassName("Date", "Kernel-Objects", Date, smalltalk.Object);
-smalltalk.wrapClassName("UndefinedObject", "Kernel-Objects", SmalltalkNil, smalltalk.Object, false);
-
-smalltalk.addClass("Collection", smalltalk.Object, null, "Kernel-Collections");
-smalltalk.addClass("IndexableCollection", smalltalk.Collection, null, "Kernel-Collections");
-smalltalk.addClass("SequenceableCollection", smalltalk.IndexableCollection, null, "Kernel-Collections");
-smalltalk.addClass("CharacterArray", smalltalk.SequenceableCollection, null, "Kernel-Collections");
-smalltalk.wrapClassName("String", "Kernel-Collections", String, smalltalk.CharacterArray);
-smalltalk.wrapClassName("Array", "Kernel-Collections", Array, smalltalk.SequenceableCollection);
-smalltalk.wrapClassName("RegularExpression", "Kernel-Collections", RegExp, smalltalk.Object);
-
-smalltalk.wrapClassName("Error", "Kernel-Exceptions", Error, smalltalk.Object);
-smalltalk.wrapClassName("MethodContext", "Kernel-Methods", SmalltalkMethodContext, smalltalk.Object, false);
-
-/* Alias definitions */
-
-smalltalk.alias(smalltalk.Array, "OrderedCollection");
-smalltalk.alias(smalltalk.Date, "Time");
+    SmalltalkMethodContext.prototype.locals = {};
+    SmalltalkMethodContext.prototype.receiver = null;
+    SmalltalkMethodContext.prototype.selector = null;
+    SmalltalkMethodContext.prototype.lookupClass = null;
+
+    inherits(SmalltalkMethodContext, SmalltalkObject);
+
+    SmalltalkMethodContext.prototype.fill = function(receiver, selector, locals, lookupClass) {
+        this.receiver    = receiver;
+        this.selector    = selector;
+        this.locals      = locals || {};
+        this.lookupClass = lookupClass;
+    };
+
+    SmalltalkMethodContext.prototype.fillBlock = function(locals, ctx) {
+        this.locals        = locals || {};
+        this.outerContext  = ctx;
+    };
+
+    SmalltalkMethodContext.prototype.init = function() {
+        var home = this.homeContext;
+        if(home) {
+            home = home.init();
+        }
+
+        this.setup(this);
+    };
+
+    SmalltalkMethodContext.prototype.method = function() {
+        var method;
+        var lookup = this.lookupClass || this.receiver.klass;
+        while(!method && lookup) {
+            method = lookup.methods[smalltalk.convertSelector(this.selector)];
+            lookup = lookup.superclass;
+        }
+        return method;
+    };
+
+    /* Global Smalltalk objects. */
+
+    var nil = new SmalltalkNil();
+    var smalltalk = new Smalltalk();
+
+    if(this.jQuery) {
+        this.jQuery.allowJavaScriptCalls = true;
+    }
+
+    /*
+     * Answer the smalltalk representation of o.
+     * Used in message sends
+     */
+
+    var _st = function(o) {
+        if(o == null) {return nil;}
+        if(o.klass) {return o;}
+        return smalltalk.JSObjectProxy._on_(o);
+    };
+
+
+    /***************************************** BOOTSTRAP ******************************************/
+
+    smalltalk.wrapClassName("Object", "Kernel-Objects", SmalltalkObject, undefined, false);
+    smalltalk.wrapClassName("Behavior", "Kernel-Classes", SmalltalkBehavior, smalltalk.Object, false);
+    smalltalk.wrapClassName("Metaclass", "Kernel-Classes", SmalltalkMetaclass, smalltalk.Behavior, false);
+    smalltalk.wrapClassName("Class", "Kernel-Classes", SmalltalkClass, smalltalk.Behavior, false);
+
+    smalltalk.Object.klass.superclass = smalltalk.Class;
+
+
+    smalltalk.wrapClassName("Smalltalk", "Kernel-Objects", Smalltalk, smalltalk.Object, false);
+    smalltalk.wrapClassName("Package", "Kernel-Objects", SmalltalkPackage, smalltalk.Object, false);
+    smalltalk.wrapClassName("CompiledMethod", "Kernel-Methods", SmalltalkMethod, smalltalk.Object, false);
+    smalltalk.wrapClassName("Organizer", "Kernel-Objects", SmalltalkOrganizer, smalltalk.Object, false);
+    smalltalk.wrapClassName("PackageOrganizer", "Kernel-Objects", SmalltalkPackageOrganizer, smalltalk.Organizer, false);
+    smalltalk.wrapClassName("ClassOrganizer", "Kernel-Objects", SmalltalkClassOrganizer, smalltalk.Organizer, false);
+
+
+    smalltalk.wrapClassName("Number", "Kernel-Objects", Number, smalltalk.Object);
+    smalltalk.wrapClassName("BlockClosure", "Kernel-Methods", Function, smalltalk.Object);
+    smalltalk.wrapClassName("Boolean", "Kernel-Objects", Boolean, smalltalk.Object);
+    smalltalk.wrapClassName("Date", "Kernel-Objects", Date, smalltalk.Object);
+    smalltalk.wrapClassName("UndefinedObject", "Kernel-Objects", SmalltalkNil, smalltalk.Object, false);
+
+    smalltalk.addClass("Collection", smalltalk.Object, null, "Kernel-Collections");
+    smalltalk.addClass("IndexableCollection", smalltalk.Collection, null, "Kernel-Collections");
+    smalltalk.addClass("SequenceableCollection", smalltalk.IndexableCollection, null, "Kernel-Collections");
+    smalltalk.addClass("CharacterArray", smalltalk.SequenceableCollection, null, "Kernel-Collections");
+    smalltalk.wrapClassName("String", "Kernel-Collections", String, smalltalk.CharacterArray);
+    smalltalk.wrapClassName("Array", "Kernel-Collections", Array, smalltalk.SequenceableCollection);
+    smalltalk.wrapClassName("RegularExpression", "Kernel-Collections", RegExp, smalltalk.Object);
+
+    smalltalk.wrapClassName("Error", "Kernel-Exceptions", Error, smalltalk.Object);
+    smalltalk.wrapClassName("MethodContext", "Kernel-Methods", SmalltalkMethodContext, smalltalk.Object, false);
+
+    /* Alias definitions */
+
+    smalltalk.alias(smalltalk.Array, "OrderedCollection");
+    smalltalk.alias(smalltalk.Date, "Time");
+
+    // TODO make this unnecessary
+    this.smalltalk = smalltalk;
+    this.nil = nil;
+    this._st = _st;
+
+    return { smalltalk: smalltalk, nil: nil, _st: _st };
+});
+

+ 3 - 0
js/lib/_st.js

@@ -0,0 +1,3 @@
+define(['amber/boot'], function () {
+    return _st;
+});

+ 3 - 3
js/lib/amber-in-browser.js

@@ -41,11 +41,11 @@ require = function (require) {
             'CodeMirror/addon/hint/show-hint': {
                 deps: [ 'CodeMirror/codemirror' ]
             },
-            'amber/boot': {
-                deps: [ 'es5-shim-2.0.2/es5-shim.min', 'es5-shim-2.0.2/es5-sham.min' ]
+            'ensure-console': {
+                exports: 'console'
             },
             'amber/Kernel-Objects.deploy': {
-                deps: [ 'smalltalk', 'nil' ]
+                deps: [ 'smalltalk', 'nil', '_st' ]
             },
             'amber/Kernel-Classes.deploy': {
                 deps: [ 'amber/Kernel-Objects.deploy' ]

+ 10 - 0
js/lib/ensure-console.js

@@ -0,0 +1,10 @@
+/* Make sure that console is defined */
+if(typeof console === "undefined") {
+	this.console = {
+		log: function() {},
+		warn: function() {},
+		info: function() {},
+		debug: function() {},
+		error: function() {}
+	};
+}