Bladeren bron

Allow JS object manipulation through #dnu: and JSObjectProxy, making printing and inspecting JS objects possible

Nicolas Petton 12 jaren geleden
bovenliggende
commit
34fca28654
4 gewijzigde bestanden met toevoegingen van 261 en 43 verwijderingen
  1. 112 11
      js/Kernel.deploy.js
  2. 101 16
      js/Kernel.js
  3. 6 4
      js/boot.js
  4. 42 12
      st/Kernel.st

+ 112 - 11
js/Kernel.deploy.js

@@ -587,6 +587,42 @@ return self;}
 }),
 smalltalk.Smalltalk);
 
+smalltalk.addMethod(
+'_basicParse_',
+smalltalk.method({
+selector: 'basicParse:',
+fn: function (aString){
+var self=this;
+return smalltalk.parser.parse(aString);
+return self;}
+}),
+smalltalk.Smalltalk);
+
+smalltalk.addMethod(
+'_parse_',
+smalltalk.method({
+selector: 'parse:',
+fn: function (aString){
+var self=this;
+var result=nil;
+smalltalk.send(self, "_try_catch_", [(function(){return result=smalltalk.send(self, "_basicParse_", [aString]);}), (function(ex){return smalltalk.send(smalltalk.send(self, "_parseError_", [ex]), "_signal", []);})]);
+return result;
+return self;}
+}),
+smalltalk.Smalltalk);
+
+smalltalk.addMethod(
+'_parseError_',
+smalltalk.method({
+selector: 'parseError:',
+fn: function (anException){
+var self=this;
+return smalltalk.Error._new()
+		._messageText_('Parse error on line ' + anException.line + ' column ' + anException.column + ' : ' + anException.message);
+return self;}
+}),
+smalltalk.Smalltalk);
+
 
 smalltalk.Smalltalk.klass.iVarNames = ['current'];
 smalltalk.addMethod(
@@ -1624,17 +1660,6 @@ return self;}
 }),
 smalltalk.BlockClosure);
 
-smalltalk.addMethod(
-'_printString',
-smalltalk.method({
-selector: 'printString',
-fn: function (){
-var self=this;
-return smalltalk.send((smalltalk.String || String), "_streamContents_", [(function(aStream){return (function($rec){smalltalk.send($rec, "_nextPutAll_", [smalltalk.send(self, "_printString", [], smalltalk.Object)]);smalltalk.send($rec, "_nextPutAll_", [unescape("%28")]);smalltalk.send($rec, "_nextPutAll_", [smalltalk.send(self, "_compiledSource", [])]);smalltalk.send($rec, "_nextPutAll_", [unescape("%29")]);return smalltalk.send($rec, "_cr", []);})(aStream);})]);
-return self;}
-}),
-smalltalk.BlockClosure);
-
 smalltalk.addMethod(
 '_whileFalse',
 smalltalk.method({
@@ -1668,6 +1693,17 @@ return self;}
 }),
 smalltalk.BlockClosure);
 
+smalltalk.addMethod(
+'_applyTo_arguments_',
+smalltalk.method({
+selector: 'applyTo:arguments:',
+fn: function (anObject, aCollection){
+var self=this;
+return self.apply(anObject, aCollection);
+return self;}
+}),
+smalltalk.BlockClosure);
+
 
 
 smalltalk.addClass('Boolean', smalltalk.Object, [], 'Kernel');
@@ -5281,3 +5317,68 @@ return self;}
 smalltalk.ErrorHandler.klass);
 
 
+smalltalk.addClass('JSObjectProxy', smalltalk.Object, ['jsObject'], 'Kernel');
+smalltalk.addMethod(
+'_jsObject_',
+smalltalk.method({
+selector: 'jsObject:',
+fn: function (aJSObject){
+var self=this;
+self['@jsObject']=aJSObject;
+return self;}
+}),
+smalltalk.JSObjectProxy);
+
+smalltalk.addMethod(
+'_jsObject',
+smalltalk.method({
+selector: 'jsObject',
+fn: function (){
+var self=this;
+return self['@jsObject'];
+return self;}
+}),
+smalltalk.JSObjectProxy);
+
+smalltalk.addMethod(
+'_printString',
+smalltalk.method({
+selector: 'printString',
+fn: function (){
+var self=this;
+return smalltalk.send(smalltalk.send(self, "_jsObject", []), "_toString", []);
+return self;}
+}),
+smalltalk.JSObjectProxy);
+
+smalltalk.addMethod(
+'_inspectOn_',
+smalltalk.method({
+selector: 'inspectOn:',
+fn: function (anInspector){
+var self=this;
+var variables=nil;
+variables=smalltalk.send((smalltalk.Dictionary || Dictionary), "_new", []);
+smalltalk.send(variables, "_at_put_", [unescape("%23self"), smalltalk.send(self, "_jsObject", [])]);
+smalltalk.send(anInspector, "_setLabel_", [smalltalk.send(self, "_printString", [])]);
+for(var i in self['@jsObject']) {
+		variables._at_put_(i, self['@jsObject'][i]);
+	};
+smalltalk.send(anInspector, "_setVariables_", [variables]);
+return self;}
+}),
+smalltalk.JSObjectProxy);
+
+
+smalltalk.addMethod(
+'_on_',
+smalltalk.method({
+selector: 'on:',
+fn: function (aJSObject){
+var self=this;
+return (function($rec){smalltalk.send($rec, "_jsObject_", [aJSObject]);return smalltalk.send($rec, "_yourself", []);})(smalltalk.send(self, "_new", []));
+return self;}
+}),
+smalltalk.JSObjectProxy.klass);
+
+

+ 101 - 16
js/Kernel.js

@@ -2232,21 +2232,6 @@ referencedClasses: []
 }),
 smalltalk.BlockClosure);
 
-smalltalk.addMethod(
-'_printString',
-smalltalk.method({
-selector: 'printString',
-category: 'printing',
-fn: function (){
-var self=this;
-return smalltalk.send((smalltalk.String || String), "_streamContents_", [(function(aStream){return (function($rec){smalltalk.send($rec, "_nextPutAll_", [smalltalk.send(self, "_printString", [], smalltalk.Object)]);smalltalk.send($rec, "_nextPutAll_", [unescape("%28")]);smalltalk.send($rec, "_nextPutAll_", [smalltalk.send(self, "_compiledSource", [])]);smalltalk.send($rec, "_nextPutAll_", [unescape("%29")]);return smalltalk.send($rec, "_cr", []);})(aStream);})]);
-return self;},
-source: unescape('printString%0A%09%5E%20String%20streamContents%3A%20%5B%3AaStream%7C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20aStream%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%09nextPutAll%3A%20super%20printString%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%09nextPutAll%3A%20%27%28%27%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%09nextPutAll%3A%20self%20compiledSource%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%09nextPutAll%3A%20%27%29%27%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%09cr.%0A%20%20%20%20%20%20%20%20%20%20%20%5D'),
-messageSends: ["streamContents:", "nextPutAll:", "printString", "compiledSource", "cr"],
-referencedClasses: [smalltalk.String]
-}),
-smalltalk.BlockClosure);
-
 smalltalk.addMethod(
 '_whileFalse',
 smalltalk.method({
@@ -2292,6 +2277,21 @@ referencedClasses: []
 }),
 smalltalk.BlockClosure);
 
+smalltalk.addMethod(
+'_applyTo_arguments_',
+smalltalk.method({
+selector: 'applyTo:arguments:',
+category: 'evaluating',
+fn: function (anObject, aCollection){
+var self=this;
+return self.apply(anObject, aCollection);
+return self;},
+source: unescape('applyTo%3A%20anObject%20arguments%3A%20aCollection%0A%09%3Creturn%20self.apply%28anObject%2C%20aCollection%29%3E'),
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.BlockClosure);
+
 
 
 smalltalk.addClass('Boolean', smalltalk.Object, [], 'Kernel');
@@ -6100,7 +6100,7 @@ smalltalk.send(self['@class'], "_addCompiledMethod_", [method]);
 return self;},
 source: unescape('compileMethod%3A%20aString%0A%09%7C%20method%20%7C%0A%09method%20%3A%3D%20Compiler%20new%20load%3A%20aString%20forClass%3A%20class.%0A%09method%20category%3A%20category.%0A%09class%20addCompiledMethod%3A%20method'),
 messageSends: ["load:forClass:", "new", "category:", "addCompiledMethod:"],
-referencedClasses: [smalltalk.Compiler]
+referencedClasses: [smalltalk.nil]
 }),
 smalltalk.ClassCategoryReader);
 
@@ -7138,3 +7138,88 @@ referencedClasses: []
 smalltalk.ErrorHandler.klass);
 
 
+smalltalk.addClass('JSObjectProxy', smalltalk.Object, ['jsObject'], 'Kernel');
+smalltalk.addMethod(
+'_jsObject_',
+smalltalk.method({
+selector: 'jsObject:',
+category: 'accessing',
+fn: function (aJSObject){
+var self=this;
+self['@jsObject']=aJSObject;
+return self;},
+source: unescape('jsObject%3A%20aJSObject%0A%09jsObject%20%3A%3D%20aJSObject'),
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.JSObjectProxy);
+
+smalltalk.addMethod(
+'_jsObject',
+smalltalk.method({
+selector: 'jsObject',
+category: 'accessing',
+fn: function (){
+var self=this;
+return self['@jsObject'];
+return self;},
+source: unescape('jsObject%0A%09%5EjsObject'),
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.JSObjectProxy);
+
+smalltalk.addMethod(
+'_printString',
+smalltalk.method({
+selector: 'printString',
+category: 'proxy',
+fn: function (){
+var self=this;
+return smalltalk.send(smalltalk.send(self, "_jsObject", []), "_toString", []);
+return self;},
+source: unescape('printString%0A%09%5Eself%20jsObject%20toString'),
+messageSends: ["toString", "jsObject"],
+referencedClasses: []
+}),
+smalltalk.JSObjectProxy);
+
+smalltalk.addMethod(
+'_inspectOn_',
+smalltalk.method({
+selector: 'inspectOn:',
+category: 'proxy',
+fn: function (anInspector){
+var self=this;
+var variables=nil;
+variables=smalltalk.send((smalltalk.Dictionary || Dictionary), "_new", []);
+smalltalk.send(variables, "_at_put_", [unescape("%23self"), smalltalk.send(self, "_jsObject", [])]);
+smalltalk.send(anInspector, "_setLabel_", [smalltalk.send(self, "_printString", [])]);
+for(var i in self['@jsObject']) {
+		variables._at_put_(i, self['@jsObject'][i]);
+	};
+smalltalk.send(anInspector, "_setVariables_", [variables]);
+return self;},
+source: unescape('inspectOn%3A%20anInspector%0A%09%7C%20variables%20%7C%0A%09variables%20%3A%3D%20Dictionary%20new.%0A%09variables%20at%3A%20%27%23self%27%20put%3A%20self%20jsObject.%0A%09anInspector%20setLabel%3A%20self%20printString.%0A%09%3Cfor%28var%20i%20in%20self%5B%27@jsObject%27%5D%29%20%7B%0A%09%09variables._at_put_%28i%2C%20self%5B%27@jsObject%27%5D%5Bi%5D%29%3B%0A%09%7D%3E.%0A%09anInspector%20setVariables%3A%20variables'),
+messageSends: ["new", "at:put:", "jsObject", "setLabel:", "printString", "setVariables:"],
+referencedClasses: [smalltalk.Dictionary]
+}),
+smalltalk.JSObjectProxy);
+
+
+smalltalk.addMethod(
+'_on_',
+smalltalk.method({
+selector: 'on:',
+category: 'instance creation',
+fn: function (aJSObject){
+var self=this;
+return (function($rec){smalltalk.send($rec, "_jsObject_", [aJSObject]);return smalltalk.send($rec, "_yourself", []);})(smalltalk.send(self, "_new", []));
+return self;},
+source: unescape('on%3A%20aJSObject%0A%09%5Eself%20new%0A%09%09jsObject%3A%20aJSObject%3B%0A%09%09yourself'),
+messageSends: ["jsObject:", "yourself", "new"],
+referencedClasses: []
+}),
+smalltalk.JSObjectProxy.klass);
+
+

+ 6 - 4
js/boot.js

@@ -209,7 +209,7 @@ function Smalltalk(){
        If the receiver does not understand the selector, call its #doesNotUnderstand: method */
 
     sendWithoutContext = function(receiver, selector, args, klass) {
-	if(typeof receiver === "undefined") {
+	if(receiver === undefined || receiver === null) {
 	    receiver = nil;
 	}
 	if(!klass && receiver.klass && receiver[selector]) {
@@ -244,7 +244,7 @@ function Smalltalk(){
 
     withContextSend = function(receiver, selector, args, klass) {
 	var call, context;
-	if(typeof receiver === "undefined") {
+	if(receiver === undefined || receiver === null) {
 	    receiver = nil;
 	}
 	if(!klass && receiver.klass && receiver[selector]) {
@@ -290,7 +290,8 @@ function Smalltalk(){
     };
 
     function callJavaScriptMethod(receiver, selector, args) {
-	/* Call a method of a JS object, or answer a property.
+	/* Call a method of a JS object, or answer a property if it exists.
+	   Else try wrapping a JSObjectProxy around the receiver.
  
 	   Converts keyword-based selectors by using the first
 	   keyword only, but keeping all message arguments.
@@ -312,7 +313,8 @@ function Smalltalk(){
 		return jsProperty
 	    }
 	}
-	smalltalk.Error._signal_(receiver + ' is not a Jtalk object and "' + jsSelector + '" is undefined')
+	
+	return st.send(st.JSObjectProxy._on_(receiver), selector, arguments);
     };
 
 

+ 42 - 12
st/Kernel.st

@@ -792,19 +792,10 @@ new
 	"Use the receiver as a JS constructor. 
 	*Do not* use this method to instanciate Smalltalk objects!!"
 	<return new self()>
-! !
-
-!BlockClosure methodsFor: 'printing'!
+!
 
-printString
-	^ String streamContents: [:aStream| 
-                                  aStream 
-                                  	nextPutAll: super printString;
-                                  	nextPutAll: '(';
-                                  	nextPutAll: self compiledSource;
-                                  	nextPutAll: ')';
-                                  	cr.
-           ]
+applyTo: anObject arguments: aCollection
+	<return self.apply(anObject, aCollection)>
 ! !
 
 !BlockClosure methodsFor: 'timeout/interval'!
@@ -2591,3 +2582,42 @@ register
 	ErrorHandler setCurrent: self new
 ! !
 
+Object subclass: #JSObjectProxy
+	instanceVariableNames: 'jsObject'
+	category: 'Kernel'!
+
+!JSObjectProxy methodsFor: 'accessing'!
+
+jsObject: aJSObject
+	jsObject := aJSObject
+!
+
+jsObject
+	^jsObject
+! !
+
+!JSObjectProxy methodsFor: 'proxy'!
+
+printString
+	^self jsObject toString
+!
+
+inspectOn: anInspector
+	| variables |
+	variables := Dictionary new.
+	variables at: '#self' put: self jsObject.
+	anInspector setLabel: self printString.
+	<for(var i in self['@jsObject']) {
+		variables._at_put_(i, self['@jsObject'][i]);
+	}>.
+	anInspector setVariables: variables
+! !
+
+!JSObjectProxy class methodsFor: 'instance creation'!
+
+on: aJSObject
+	^self new
+		jsObject: aJSObject;
+		yourself
+! !
+