Browse Source

SemanticAnalyzer: check in package scope. Fix #1150.

Herbert Vojčík 9 years ago
parent
commit
4be78f87a1
4 changed files with 138 additions and 28 deletions
  1. 51 8
      src/Compiler-Core.js
  2. 14 3
      src/Compiler-Core.st
  3. 59 13
      src/Compiler-Semantic.js
  4. 14 4
      src/Compiler-Semantic.st

+ 51 - 8
src/Compiler-Core.js

@@ -4,7 +4,7 @@ $core.addPackage('Compiler-Core');
 $core.packages["Compiler-Core"].innerEval = function (expr) { return eval(expr); };
 $core.packages["Compiler-Core"].transport = {"type":"amd","amdNamespace":"amber_core"};
 
-$core.addClass('AbstractCodeGenerator', $globals.Object, ['currentClass', 'source'], 'Compiler-Core');
+$core.addClass('AbstractCodeGenerator', $globals.Object, ['currentClass', 'currentPackage', 'source'], 'Compiler-Core');
 //>>excludeStart("ide", pragmas.excludeIdeData);
 $globals.AbstractCodeGenerator.comment="I am the abstract super class of all code generators and provide their common API.";
 //>>excludeEnd("ide");
@@ -110,6 +110,45 @@ messageSends: []
 }),
 $globals.AbstractCodeGenerator);
 
+$core.addMethod(
+$core.method({
+selector: "currentPackage",
+protocol: 'accessing',
+fn: function (){
+var self=this;
+var $1;
+$1=self["@currentPackage"];
+return $1;
+
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "currentPackage\x0a\x09^ currentPackage",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: []
+}),
+$globals.AbstractCodeGenerator);
+
+$core.addMethod(
+$core.method({
+selector: "currentPackage:",
+protocol: 'accessing',
+fn: function (anObject){
+var self=this;
+self["@currentPackage"]=anObject;
+return self;
+
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["anObject"],
+source: "currentPackage: anObject\x0a\x09currentPackage := anObject",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: []
+}),
+$globals.AbstractCodeGenerator);
+
 $core.addMethod(
 $core.method({
 selector: "pseudoVariables",
@@ -265,8 +304,11 @@ function $SemanticAnalyzer(){return $globals.SemanticAnalyzer||(typeof SemanticA
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
-var $1;
-$1=$recv($SemanticAnalyzer())._on_(self._currentClass());
+var $2,$3,$1;
+$2=$recv($SemanticAnalyzer())._on_(self._currentClass());
+$recv($2)._thePackage_(self._currentPackage());
+$3=$recv($2)._yourself();
+$1=$3;
 return $1;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 }, function($ctx1) {$ctx1.fill(self,"semanticAnalyzer",{},$globals.CodeGenerator)});
@@ -274,10 +316,10 @@ return $1;
 },
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: [],
-source: "semanticAnalyzer\x0a\x09^ SemanticAnalyzer on: self currentClass",
+source: "semanticAnalyzer\x0a\x09^ (SemanticAnalyzer on: self currentClass)\x0a\x09\x09thePackage: self currentPackage;\x0a\x09\x09yourself",
 referencedClasses: ["SemanticAnalyzer"],
 //>>excludeEnd("ide");
-messageSends: ["on:", "currentClass"]
+messageSends: ["thePackage:", "on:", "currentClass", "currentPackage", "yourself"]
 }),
 $globals.CodeGenerator);
 
@@ -437,7 +479,8 @@ var $1,$2,$3;
 generator=$recv(self._codeGeneratorClass())._new();
 $1=generator;
 $recv($1)._source_(self._source());
-$2=$recv($1)._currentClass_(self._currentClass());
+$recv($1)._currentClass_(self._currentClass());
+$2=$recv($1)._currentPackage_(self._currentPackage());
 result=$recv(generator)._compileNode_(aNode);
 self._unknownVariables_([]);
 $3=result;
@@ -448,10 +491,10 @@ return $3;
 },
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: ["aNode"],
-source: "compileNode: aNode\x0a\x09| generator result |\x0a\x09generator := self codeGeneratorClass new.\x0a\x09generator\x0a\x09\x09source: self source;\x0a\x09\x09currentClass: self currentClass.\x0a\x09result := generator compileNode: aNode.\x0a\x09self unknownVariables: #().\x0a\x09^ result",
+source: "compileNode: aNode\x0a\x09| generator result |\x0a\x09generator := self codeGeneratorClass new.\x0a\x09generator\x0a\x09\x09source: self source;\x0a\x09\x09currentClass: self currentClass;\x0a\x09\x09currentPackage: self currentPackage.\x0a\x09result := generator compileNode: aNode.\x0a\x09self unknownVariables: #().\x0a\x09^ result",
 referencedClasses: [],
 //>>excludeEnd("ide");
-messageSends: ["new", "codeGeneratorClass", "source:", "source", "currentClass:", "currentClass", "compileNode:", "unknownVariables:"]
+messageSends: ["new", "codeGeneratorClass", "source:", "source", "currentClass:", "currentClass", "currentPackage:", "currentPackage", "compileNode:", "unknownVariables:"]
 }),
 $globals.Compiler);
 

+ 14 - 3
src/Compiler-Core.st

@@ -1,6 +1,6 @@
 Smalltalk createPackage: 'Compiler-Core'!
 Object subclass: #AbstractCodeGenerator
-	instanceVariableNames: 'currentClass source'
+	instanceVariableNames: 'currentClass currentPackage source'
 	package: 'Compiler-Core'!
 !AbstractCodeGenerator commentStamp!
 I am the abstract super class of all code generators and provide their common API.!
@@ -24,6 +24,14 @@ currentClass: aClass
 	currentClass := aClass
 !
 
+currentPackage
+	^ currentPackage
+!
+
+currentPackage: anObject
+	currentPackage := anObject
+!
+
 pseudoVariables
 	^ Smalltalk pseudoVariableNames
 !
@@ -66,7 +74,9 @@ irTranslator
 !
 
 semanticAnalyzer
-	^ SemanticAnalyzer on: self currentClass
+	^ (SemanticAnalyzer on: self currentClass)
+		thePackage: self currentPackage;
+		yourself
 !
 
 translator
@@ -149,7 +159,8 @@ compileNode: aNode
 	generator := self codeGeneratorClass new.
 	generator
 		source: self source;
-		currentClass: self currentClass.
+		currentClass: self currentClass;
+		currentPackage: self currentPackage.
 	result := generator compileNode: aNode.
 	self unknownVariables: #().
 	^ result

+ 59 - 13
src/Compiler-Semantic.js

@@ -1759,7 +1759,7 @@ $globals.UnknownVar);
 
 
 
-$core.addClass('SemanticAnalyzer', $globals.NodeVisitor, ['currentScope', 'blockIndex', 'theClass', 'classReferences', 'messageSends', 'superSends'], 'Compiler-Semantic');
+$core.addClass('SemanticAnalyzer', $globals.NodeVisitor, ['currentScope', 'blockIndex', 'thePackage', 'theClass', 'classReferences', 'messageSends', 'superSends'], 'Compiler-Semantic');
 //>>excludeStart("ide", pragmas.excludeIdeData);
 $globals.SemanticAnalyzer.comment="I semantically analyze the abstract syntax tree and annotate it with informations such as non local returns and variable scopes.";
 //>>excludeEnd("ide");
@@ -1844,7 +1844,7 @@ $1=$recv($recv($recv($recv($Smalltalk())._globalJsVariables())._includes_(identi
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx2) {
 //>>excludeEnd("ctx");
-return self._isVariableGloballyUndefined_(identifier);
+return self._isVariableUndefined_inPackage_(identifier,self._thePackage());
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
 //>>excludeEnd("ctx");
@@ -1869,34 +1869,41 @@ return self;
 },
 //>>excludeStart("ide", pragmas.excludeIdeData);
 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 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 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 ]",
+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 globalJsVariables includes: identifier) not\x0a\x09\x09and: [ self isVariableUndefined: identifier inPackage: self thePackage ])\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 ]",
 referencedClasses: ["Smalltalk", "UnknownVariableError"],
 //>>excludeEnd("ide");
-messageSends: ["value", "ifTrue:ifFalse:", "and:", "not", "includes:", "globalJsVariables", "isVariableGloballyUndefined:", "variableName:", "new", "signal", "add:", "unknownVariables", "methodScope"]
+messageSends: ["value", "ifTrue:ifFalse:", "and:", "not", "includes:", "globalJsVariables", "isVariableUndefined:inPackage:", "thePackage", "variableName:", "new", "signal", "add:", "unknownVariables", "methodScope"]
 }),
 $globals.SemanticAnalyzer);
 
 $core.addMethod(
 $core.method({
-selector: "isVariableGloballyUndefined:",
+selector: "isVariableUndefined:inPackage:",
 protocol: 'testing',
-fn: function (aString){
+fn: function (aString,aPackage){
 var self=this;
+function $Compiler(){return $globals.Compiler||(typeof Compiler=="undefined"?nil:Compiler)}
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
-return eval('typeof ' + aString + ' == "undefined"');
-return self;
+var $2,$3,$1;
+$2=$recv($Compiler())._new();
+$3=$recv("typeof ".__comma(aString)).__comma(" == \x22undefined\x22");
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx1) {$ctx1.fill(self,"isVariableGloballyUndefined:",{aString:aString},$globals.SemanticAnalyzer)});
+$ctx1.sendIdx[","]=1;
+//>>excludeEnd("ctx");
+$1=$recv($2)._eval_forPackage_($3,aPackage);
+return $1;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"isVariableUndefined:inPackage:",{aString:aString,aPackage:aPackage},$globals.SemanticAnalyzer)});
 //>>excludeEnd("ctx");
 },
 //>>excludeStart("ide", pragmas.excludeIdeData);
-args: ["aString"],
-source: "isVariableGloballyUndefined: aString\x0a\x09<return eval('typeof ' + aString + ' == \x22undefined\x22')>",
-referencedClasses: [],
+args: ["aString", "aPackage"],
+source: "isVariableUndefined: aString inPackage: aPackage\x0a\x09^ Compiler new\x0a\x09\x09eval: 'typeof ', aString, ' == \x22undefined\x22'\x0a\x09\x09forPackage: aPackage",
+referencedClasses: ["Compiler"],
 //>>excludeEnd("ide");
-messageSends: []
+messageSends: ["eval:forPackage:", "new", ","]
 }),
 $globals.SemanticAnalyzer);
 
@@ -2172,6 +2179,45 @@ messageSends: []
 }),
 $globals.SemanticAnalyzer);
 
+$core.addMethod(
+$core.method({
+selector: "thePackage",
+protocol: 'accessing',
+fn: function (){
+var self=this;
+var $1;
+$1=self["@thePackage"];
+return $1;
+
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "thePackage\x0a\x09^ thePackage",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: []
+}),
+$globals.SemanticAnalyzer);
+
+$core.addMethod(
+$core.method({
+selector: "thePackage:",
+protocol: 'accessing',
+fn: function (aPackage){
+var self=this;
+self["@thePackage"]=aPackage;
+return self;
+
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["aPackage"],
+source: "thePackage: aPackage\x0a\x09thePackage := aPackage",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: []
+}),
+$globals.SemanticAnalyzer);
+
 $core.addMethod(
 $core.method({
 selector: "validateVariableScope:",

+ 14 - 4
src/Compiler-Semantic.st

@@ -404,7 +404,7 @@ isUnknownVar
 ! !
 
 NodeVisitor subclass: #SemanticAnalyzer
-	instanceVariableNames: 'currentScope blockIndex theClass classReferences messageSends superSends'
+	instanceVariableNames: 'currentScope blockIndex thePackage theClass classReferences messageSends superSends'
 	package: 'Compiler-Semantic'!
 !SemanticAnalyzer commentStamp!
 I semantically analyze the abstract syntax tree and annotate it with informations such as non local returns and variable scopes.!
@@ -429,6 +429,14 @@ theClass
 
 theClass: aClass
 	theClass := aClass
+!
+
+thePackage
+	^ thePackage
+!
+
+thePackage: aPackage
+	thePackage := aPackage
 ! !
 
 !SemanticAnalyzer methodsFor: 'error handling'!
@@ -452,7 +460,7 @@ errorUnknownVariable: aNode
 	identifier := aNode value.
 	
 	((Smalltalk globalJsVariables includes: identifier) not
-		and: [ self isVariableGloballyUndefined: identifier ])
+		and: [ self isVariableUndefined: identifier inPackage: self thePackage ])
 			ifTrue: [
 				UnknownVariableError new
 					variableName: aNode value;
@@ -507,8 +515,10 @@ validateVariableScope: aString
 
 !SemanticAnalyzer methodsFor: 'testing'!
 
-isVariableGloballyUndefined: aString
-	<return eval('typeof ' + aString + ' == "undefined"')>
+isVariableUndefined: aString inPackage: aPackage
+	^ Compiler new
+		eval: 'typeof ', aString, ' == "undefined"'
+		forPackage: aPackage
 ! !
 
 !SemanticAnalyzer methodsFor: 'visiting'!