Browse Source

Merge pull request #523 from mkroehnert/compiler

Add support for managing JS global variables needed by the compiler
Nicolas Petton 11 năm trước cách đây
mục cha
commit
1d9d83c745

+ 3 - 0
API-CHANGES.txt

@@ -52,6 +52,9 @@
 + Environment>>removeProtocol:
 + Environment>>renameProtocol:to:in:
 + CompiledMethod>>protocol:
++ Smalltalk>>globalJsVariables
++ Smalltalk>>addGlobalJsVariable:
++ Smalltalk>>deleteGlobalJsVariable:
 
 
 - Object>>storeOn:

+ 7 - 0
bin/amberc

@@ -41,6 +41,9 @@ function handle_options(optionsArray, amber_dir) {
 			case '-l':
 				defaults.load.push.apply(defaults.load, optionsArray.shift().split(','));
 				break;
+			case '-g':
+				defaults.jsGlobals.push.apply(defaults.jsGlobals, optionsArray.shift().split(','));
+				break;
 			case '-i':
 				defaults.init = optionsArray.shift();
 				break;
@@ -139,6 +142,10 @@ function print_usage() {
 	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('  -g jsGlobal1,jsGlobal2');
+	console.log('     Comma separated list of JS global variable names.');
+	console.log('     The names are added to a list containing "window", "document" and others.');
+	console.log('');
 	console.log('  -i init_file');
 	console.log('     Add library initializer <init_file> instead of default $AMBER/js/init.js ');
 	console.log('');

+ 6 - 0
bin/amberc.js

@@ -106,6 +106,7 @@ var createDefaults = function(amber_dir, finished_callback){
 		'mainfile': undefined,
 		'stFiles': [],
 		'jsFiles': [],
+		'jsGlobals': [],
 		'closure': false,
 		'closure_parts': false,
 		'closure_full': false,
@@ -422,6 +423,11 @@ AmberC.prototype.create_compiler = function(compilerFilesArray) {
 		console.log('Compiler loaded');
 		self.defaults.smalltalk.ErrorHandler._setCurrent_(self.defaults.smalltalk.RethrowErrorHandler._new());
 
+		if(0 != self.defaults.jsGlobals.length) {
+			var jsGlobalVariables = self.defaults.smalltalk.globalJsVariables;
+			jsGlobalVariables.push.apply(jsGlobalVariables, self.defaults.jsGlobals);
+		}
+
 		self.compile();
 	});
 

+ 67 - 8
cli/js/amber-cli.js

@@ -128,6 +128,8 @@ function Smalltalk() {
 		'implements', 'interface', 'let', 'package', 'private', 'protected',
 		'public', 'static', 'yield'];
 
+	st.globalJsVariables = ['jQuery', 'window', 'document', 'process', 'global'];
+
 	var initialized = false;
 
 	/* Smalltalk classes */
@@ -5964,6 +5966,25 @@ smalltalk.Random);
 
 smalltalk.addClass('Smalltalk', smalltalk.Object, [], 'Kernel-Objects');
 smalltalk.Smalltalk.comment="I represent the global JavaScript variable `smalltalk` declared in `js/boot.js`.\x0a\x0a## API\x0a\x0aI have only one instance, accessed with class-side method `#current`.\x0a\x0aThe `smalltalk` object holds all class and packages defined in the system.\x0a\x0a## Classes\x0a\x0aClasses can be accessed using the following methods:\x0a\x0a- `#classes` answers the full list of Smalltalk classes in the system\x0a- `#at:` answers a specific class or `nil`\x0a\x0a## Packages\x0a\x0aPackages can be accessed using the following methods:\x0a\x0a- `#packages` answers the full list of packages\x0a- `#packageAt:` answers a specific package or `nil`\x0a\x0a## Parsing\x0a\x0aThe `#parse:` method is used to parse Amber source code.\x0aIt requires the `Compiler` package and the `js/parser.js` parser file in order to work.";
+smalltalk.addMethod(
+smalltalk.method({
+selector: "addGlobalJsVariable:",
+category: 'globals',
+fn: function (aString){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+_st(self._globalJsVariables())._add_(aString);
+$1=self;
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"addGlobalJsVariable:",{aString:aString},smalltalk.Smalltalk)})},
+args: ["aString"],
+source: "addGlobalJsVariable: aString\x0a\x09self globalJsVariables add: aString.\x0a\x09^self",
+messageSends: ["add:", "globalJsVariables"],
+referencedClasses: []
+}),
+smalltalk.Smalltalk);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "asSmalltalkException:",
@@ -6095,6 +6116,27 @@ referencedClasses: []
 }),
 smalltalk.Smalltalk);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "deleteGlobalJsVariable:",
+category: 'globals',
+fn: function (aString){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+_st(self._globalJsVariables())._remove_ifAbsent_(aString,(function(){
+return smalltalk.withContext(function($ctx2) {
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
+$1=self;
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"deleteGlobalJsVariable:",{aString:aString},smalltalk.Smalltalk)})},
+args: ["aString"],
+source: "deleteGlobalJsVariable: aString\x0a\x09self globalJsVariables remove: aString ifAbsent:[].\x0a\x09^self",
+messageSends: ["remove:ifAbsent:", "globalJsVariables"],
+referencedClasses: []
+}),
+smalltalk.Smalltalk);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "deletePackage:",
@@ -6111,6 +6153,22 @@ referencedClasses: []
 }),
 smalltalk.Smalltalk);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "globalJsVariables",
+category: 'globals',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return self.globalJsVariables;
+return self}, function($ctx1) {$ctx1.fill(self,"globalJsVariables",{},smalltalk.Smalltalk)})},
+args: [],
+source: "globalJsVariables\x0a\x09\x22Array of global JavaScript variables\x22\x0a\x09<return self.globalJsVariables>",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.Smalltalk);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "isSmalltalkObject:",
@@ -13519,7 +13577,7 @@ var self=this;
 return smalltalk.withContext(function($ctx1) { 
 
 		for(var i=0;i<self.length;i++) {
-			if(self[i] == anObject) {
+			if(_st(self[i]).__eq(anObject)) {
 				self.splice(i,1);
 				return self;
 			}
@@ -13528,7 +13586,7 @@ return smalltalk.withContext(function($ctx1) {
 	;
 return self}, function($ctx1) {$ctx1.fill(self,"remove:ifAbsent:",{anObject:anObject,aBlock:aBlock},smalltalk.Array)})},
 args: ["anObject", "aBlock"],
-source: "remove: anObject ifAbsent: aBlock\x0a\x09<\x0a\x09\x09for(var i=0;i<self.length;i++) {\x0a\x09\x09\x09if(self[i] == anObject) {\x0a\x09\x09\x09\x09self.splice(i,1);\x0a\x09\x09\x09\x09return self;\x0a\x09\x09\x09}\x0a\x09\x09};\x0a\x09\x09aBlock._value();\x0a\x09>",
+source: "remove: anObject ifAbsent: aBlock\x0a\x09<\x0a\x09\x09for(var i=0;i<self.length;i++) {\x0a\x09\x09\x09if(_st(self[i]).__eq(anObject)) {\x0a\x09\x09\x09\x09self.splice(i,1);\x0a\x09\x09\x09\x09return self;\x0a\x09\x09\x09}\x0a\x09\x09};\x0a\x09\x09aBlock._value();\x0a\x09>",
 messageSends: [],
 referencedClasses: []
 }),
@@ -15237,7 +15295,7 @@ return smalltalk.withContext(function($ctx1) {
 
 		var found;
 		for(var i=0; i < self['@elements'].length; i++) {
-			if(anObject == self['@elements'][i]) {
+			if(_st(anObject).__eq(self['@elements'][i])) {
 				found = true;
 				break;
 			}
@@ -15246,7 +15304,7 @@ return smalltalk.withContext(function($ctx1) {
 	;
 return self}, function($ctx1) {$ctx1.fill(self,"add:",{anObject:anObject},smalltalk.Set)})},
 args: ["anObject"],
-source: "add: anObject\x0a\x09<\x0a\x09\x09var found;\x0a\x09\x09for(var i=0; i < self['@elements'].length; i++) {\x0a\x09\x09\x09if(anObject == self['@elements'][i]) {\x0a\x09\x09\x09\x09found = true;\x0a\x09\x09\x09\x09break;\x0a\x09\x09\x09}\x0a\x09\x09}\x0a\x09\x09if(!found) {self['@elements'].push(anObject)}\x0a\x09>",
+source: "add: anObject\x0a\x09<\x0a\x09\x09var found;\x0a\x09\x09for(var i=0; i < self['@elements'].length; i++) {\x0a\x09\x09\x09if(_st(anObject).__eq(self['@elements'][i])) {\x0a\x09\x09\x09\x09found = true;\x0a\x09\x09\x09\x09break;\x0a\x09\x09\x09}\x0a\x09\x09}\x0a\x09\x09if(!found) {self['@elements'].push(anObject)}\x0a\x09>",
 messageSends: [],
 referencedClasses: []
 }),
@@ -27716,10 +27774,11 @@ fn: function (aNode){
 var self=this;
 var identifier;
 function $UnknownVariableError(){return smalltalk.UnknownVariableError||(typeof UnknownVariableError=="undefined"?nil:UnknownVariableError)}
+function $Smalltalk(){return smalltalk.Smalltalk||(typeof Smalltalk=="undefined"?nil:Smalltalk)}
 return smalltalk.withContext(function($ctx1) { 
 var $1,$2,$3;
 identifier=_st(aNode)._value();
-$1=_st(_st(["jQuery", "window", "document", "process", "global"]._includes_(identifier))._not())._and_((function(){
+$1=_st(_st(_st(_st(_st($Smalltalk())._current())._globalJsVariables())._includes_(identifier))._not())._and_((function(){
 return smalltalk.withContext(function($ctx2) {
 return self._isVariableGloballyUndefined_(identifier);
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
@@ -27733,9 +27792,9 @@ _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\x09We allow four variable names in addition: `jQuery`, `window`, `process` and `global`\x0a\x09for nodejs and browser environments.\x0a\x09\x0a\x09This is only to make sure compilation works on both browser-based and nodejs environments.\x0a\x09The ideal solution would be to use a pragma instead\x22\x0a\x0a\x09| identifier |\x0a\x09identifier := aNode value.\x0a\x09\x0a\x09((#('jQuery' 'window' 'document' 'process' 'global') includes: identifier) not\x0a\x09\x09and: [ self isVariableGloballyUndefined: identifier ])\x0a\x09\x09\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"]
+source: "errorUnknownVariable: aNode\x0a\x09\x22Throw an error if the variable is undeclared in the global JS scope (i.e. window).\x0a\x09We allow all variables listed by Smalltalk>>#globalJsVariables.\x0a\x09This list includes: `jQuery`, `window`, `document`,  `process` and `global`\x0a\x09for nodejs and browser environments.\x0a\x09\x0a\x09This is only to make sure compilation works on both browser-based and nodejs environments.\x0a\x09The ideal solution would be to use a pragma instead\x22\x0a\x0a\x09| identifier |\x0a\x09identifier := aNode value.\x0a\x09\x0a\x09((Smalltalk current globalJsVariables includes: identifier) not\x0a\x09\x09and: [ self isVariableGloballyUndefined: identifier ])\x0a\x09\x09\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:", "globalJsVariables", "current"],
+referencedClasses: ["UnknownVariableError", "Smalltalk"]
 }),
 smalltalk.SemanticAnalyzer);
 

+ 6 - 2
grunt/tasks/grunt-amberc.js

@@ -15,14 +15,15 @@ module.exports = function(grunt) {
        helloWorld: {
          src: ['projects/HelloWorld/st/HelloWorld.st'], // REQUIRED
          output_dir: 'projects/HelloWorld/js',  // optional
+         libraries: 'Canvas',                   // optional
+         jsGlobals: ['global1', 'global2'],     // 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
+         library_suffix: '-0.9'                // optional
        },
      },
 
@@ -112,6 +113,9 @@ module.exports = function(grunt) {
     if (undefined !== data.output_dir) {
       configuration.output_dir = data.output_dir;
     }
+    if (undefined !== data.jsGlobals) {
+      configuration.jsGlobals.push.apply(configuration.jsGlobals, data.jsGlobals);
+    }
     configuration.verbose = data.verbose;
     return configuration;
   }

+ 3 - 2
js/Compiler-Semantic.deploy.js

@@ -941,10 +941,11 @@ fn: function (aNode){
 var self=this;
 var identifier;
 function $UnknownVariableError(){return smalltalk.UnknownVariableError||(typeof UnknownVariableError=="undefined"?nil:UnknownVariableError)}
+function $Smalltalk(){return smalltalk.Smalltalk||(typeof Smalltalk=="undefined"?nil:Smalltalk)}
 return smalltalk.withContext(function($ctx1) { 
 var $1,$2,$3;
 identifier=_st(aNode)._value();
-$1=_st(_st(["jQuery", "window", "document", "process", "global"]._includes_(identifier))._not())._and_((function(){
+$1=_st(_st(_st(_st(_st($Smalltalk())._current())._globalJsVariables())._includes_(identifier))._not())._and_((function(){
 return smalltalk.withContext(function($ctx2) {
 return self._isVariableGloballyUndefined_(identifier);
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
@@ -957,7 +958,7 @@ $3;
 _st(_st(_st(self["@currentScope"])._methodScope())._unknownVariables())._add_(_st(aNode)._value());
 };
 return self}, function($ctx1) {$ctx1.fill(self,"errorUnknownVariable:",{aNode:aNode,identifier:identifier},smalltalk.SemanticAnalyzer)})},
-messageSends: ["value", "ifTrue:ifFalse:", "variableName:", "new", "signal", "add:", "unknownVariables", "methodScope", "and:", "isVariableGloballyUndefined:", "not", "includes:"]}),
+messageSends: ["value", "ifTrue:ifFalse:", "variableName:", "new", "signal", "add:", "unknownVariables", "methodScope", "and:", "isVariableGloballyUndefined:", "not", "includes:", "globalJsVariables", "current"]}),
 smalltalk.SemanticAnalyzer);
 
 smalltalk.addMethod(

+ 5 - 4
js/Compiler-Semantic.js

@@ -1268,10 +1268,11 @@ fn: function (aNode){
 var self=this;
 var identifier;
 function $UnknownVariableError(){return smalltalk.UnknownVariableError||(typeof UnknownVariableError=="undefined"?nil:UnknownVariableError)}
+function $Smalltalk(){return smalltalk.Smalltalk||(typeof Smalltalk=="undefined"?nil:Smalltalk)}
 return smalltalk.withContext(function($ctx1) { 
 var $1,$2,$3;
 identifier=_st(aNode)._value();
-$1=_st(_st(["jQuery", "window", "document", "process", "global"]._includes_(identifier))._not())._and_((function(){
+$1=_st(_st(_st(_st(_st($Smalltalk())._current())._globalJsVariables())._includes_(identifier))._not())._and_((function(){
 return smalltalk.withContext(function($ctx2) {
 return self._isVariableGloballyUndefined_(identifier);
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
@@ -1285,9 +1286,9 @@ _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\x09We allow four variable names in addition: `jQuery`, `window`, `process` and `global`\x0a\x09for nodejs and browser environments.\x0a\x09\x0a\x09This is only to make sure compilation works on both browser-based and nodejs environments.\x0a\x09The ideal solution would be to use a pragma instead\x22\x0a\x0a\x09| identifier |\x0a\x09identifier := aNode value.\x0a\x09\x0a\x09((#('jQuery' 'window' 'document' 'process' 'global') includes: identifier) not\x0a\x09\x09and: [ self isVariableGloballyUndefined: identifier ])\x0a\x09\x09\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"]
+source: "errorUnknownVariable: aNode\x0a\x09\x22Throw an error if the variable is undeclared in the global JS scope (i.e. window).\x0a\x09We allow all variables listed by Smalltalk>>#globalJsVariables.\x0a\x09This list includes: `jQuery`, `window`, `document`,  `process` and `global`\x0a\x09for nodejs and browser environments.\x0a\x09\x0a\x09This is only to make sure compilation works on both browser-based and nodejs environments.\x0a\x09The ideal solution would be to use a pragma instead\x22\x0a\x0a\x09| identifier |\x0a\x09identifier := aNode value.\x0a\x09\x0a\x09((Smalltalk current globalJsVariables includes: identifier) not\x0a\x09\x09and: [ self isVariableGloballyUndefined: identifier ])\x0a\x09\x09\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:", "globalJsVariables", "current"],
+referencedClasses: ["UnknownVariableError", "Smalltalk"]
 }),
 smalltalk.SemanticAnalyzer);
 

+ 35 - 0
js/Kernel-Objects.deploy.js

@@ -3719,6 +3719,17 @@ smalltalk.Random);
 
 
 smalltalk.addClass('Smalltalk', smalltalk.Object, [], 'Kernel-Objects');
+smalltalk.addMethod(
+smalltalk.method({
+selector: "addGlobalJsVariable:",
+fn: function (aString){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+_st(self._globalJsVariables())._add_(aString);
+return self}, function($ctx1) {$ctx1.fill(self,"addGlobalJsVariable:",{aString:aString},smalltalk.Smalltalk)})},
+messageSends: ["add:", "globalJsVariables"]}),
+smalltalk.Smalltalk);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "asSmalltalkException:",
@@ -3815,6 +3826,19 @@ return self}, function($ctx1) {$ctx1.fill(self,"deleteClass:",{aClass:aClass},sm
 messageSends: []}),
 smalltalk.Smalltalk);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "deleteGlobalJsVariable:",
+fn: function (aString){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+_st(self._globalJsVariables())._remove_ifAbsent_(aString,(function(){
+return smalltalk.withContext(function($ctx2) {
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"deleteGlobalJsVariable:",{aString:aString},smalltalk.Smalltalk)})},
+messageSends: ["remove:ifAbsent:", "globalJsVariables"]}),
+smalltalk.Smalltalk);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "deletePackage:",
@@ -3826,6 +3850,17 @@ return self}, function($ctx1) {$ctx1.fill(self,"deletePackage:",{packageName:pac
 messageSends: []}),
 smalltalk.Smalltalk);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "globalJsVariables",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return self.globalJsVariables;
+return self}, function($ctx1) {$ctx1.fill(self,"globalJsVariables",{},smalltalk.Smalltalk)})},
+messageSends: []}),
+smalltalk.Smalltalk);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "isSmalltalkObject:",

+ 50 - 0
js/Kernel-Objects.js

@@ -5079,6 +5079,22 @@ smalltalk.Random);
 
 smalltalk.addClass('Smalltalk', smalltalk.Object, [], 'Kernel-Objects');
 smalltalk.Smalltalk.comment="I represent the global JavaScript variable `smalltalk` declared in `js/boot.js`.\x0a\x0a## API\x0a\x0aI have only one instance, accessed with class-side method `#current`.\x0a\x0aThe `smalltalk` object holds all class and packages defined in the system.\x0a\x0a## Classes\x0a\x0aClasses can be accessed using the following methods:\x0a\x0a- `#classes` answers the full list of Smalltalk classes in the system\x0a- `#at:` answers a specific class or `nil`\x0a\x0a## Packages\x0a\x0aPackages can be accessed using the following methods:\x0a\x0a- `#packages` answers the full list of packages\x0a- `#packageAt:` answers a specific package or `nil`\x0a\x0a## Parsing\x0a\x0aThe `#parse:` method is used to parse Amber source code.\x0aIt requires the `Compiler` package and the `js/parser.js` parser file in order to work.";
+smalltalk.addMethod(
+smalltalk.method({
+selector: "addGlobalJsVariable:",
+category: 'globals',
+fn: function (aString){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+_st(self._globalJsVariables())._add_(aString);
+return self}, function($ctx1) {$ctx1.fill(self,"addGlobalJsVariable:",{aString:aString},smalltalk.Smalltalk)})},
+args: ["aString"],
+source: "addGlobalJsVariable: aString\x0a\x09self globalJsVariables add: aString",
+messageSends: ["add:", "globalJsVariables"],
+referencedClasses: []
+}),
+smalltalk.Smalltalk);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "asSmalltalkException:",
@@ -5210,6 +5226,24 @@ referencedClasses: []
 }),
 smalltalk.Smalltalk);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "deleteGlobalJsVariable:",
+category: 'globals',
+fn: function (aString){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+_st(self._globalJsVariables())._remove_ifAbsent_(aString,(function(){
+return smalltalk.withContext(function($ctx2) {
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"deleteGlobalJsVariable:",{aString:aString},smalltalk.Smalltalk)})},
+args: ["aString"],
+source: "deleteGlobalJsVariable: aString\x0a\x09self globalJsVariables remove: aString ifAbsent:[]",
+messageSends: ["remove:ifAbsent:", "globalJsVariables"],
+referencedClasses: []
+}),
+smalltalk.Smalltalk);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "deletePackage:",
@@ -5226,6 +5260,22 @@ referencedClasses: []
 }),
 smalltalk.Smalltalk);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "globalJsVariables",
+category: 'globals',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return self.globalJsVariables;
+return self}, function($ctx1) {$ctx1.fill(self,"globalJsVariables",{},smalltalk.Smalltalk)})},
+args: [],
+source: "globalJsVariables\x0a\x09\x22Array of global JavaScript variables\x22\x0a\x09<return self.globalJsVariables>",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.Smalltalk);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "isSmalltalkObject:",

+ 2 - 0
js/boot.js

@@ -128,6 +128,8 @@ function Smalltalk() {
 		'implements', 'interface', 'let', 'package', 'private', 'protected',
 		'public', 'static', 'yield'];
 
+	st.globalJsVariables = ['jQuery', 'window', 'document', 'process', 'global'];
+
 	var initialized = false;
 
 	/* Smalltalk classes */

+ 3 - 2
st/Compiler-Semantic.st

@@ -417,7 +417,8 @@ errorShadowingVariable: aString
 
 errorUnknownVariable: aNode
 	"Throw an error if the variable is undeclared in the global JS scope (i.e. window).
-	We allow four variable names in addition: `jQuery`, `window`, `process` and `global`
+	We allow all variables listed by Smalltalk>>#globalJsVariables.
+	This list includes: `jQuery`, `window`, `document`,  `process` and `global`
 	for nodejs and browser environments.
 	
 	This is only to make sure compilation works on both browser-based and nodejs environments.
@@ -426,7 +427,7 @@ errorUnknownVariable: aNode
 	| identifier |
 	identifier := aNode value.
 	
-	((#('jQuery' 'window' 'document' 'process' 'global') includes: identifier) not
+	((Smalltalk current globalJsVariables includes: identifier) not
 		and: [ self isVariableGloballyUndefined: identifier ])
 			ifTrue: [
 				UnknownVariableError new

+ 15 - 0
st/Kernel-Objects.st

@@ -1875,6 +1875,21 @@ parseError: anException parsing: aString
 	^ ParseError new messageText: 'Parse error on line ', (anException basicAt: 'line') ,' column ' , (anException basicAt: 'column') ,' : Unexpected character ', (anException basicAt: 'found')
 ! !
 
+!Smalltalk methodsFor: 'globals'!
+
+addGlobalJsVariable: aString
+	self globalJsVariables add: aString
+!
+
+deleteGlobalJsVariable: aString
+	self globalJsVariables remove: aString ifAbsent:[]
+!
+
+globalJsVariables
+	"Array of global JavaScript variables"
+	<return self.globalJsVariables>
+! !
+
 !Smalltalk methodsFor: 'packages'!
 
 createPackage: packageName