[Reflectivity] Support for primitive methods

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

[Reflectivity] Support for primitive methods

Marcus Denker-4
Hi,

One thing I added over the last weeks: support to put links on primitives.

1) fail code. You can put links into the smalltalk code that is executed in case of primitive failure.
    For all primitive methods, we generate code immediately when setting the link, we do not wait
    till the method is called. (this is in there since a while).

2) links on the method node itself. What we do here is to wrap the original method.

It is interesting how simple this was to do:

1) when generating code for a ReflectiveMethod, check if it is a primitive:

compileAndInstallCompiledMethod
        "we install the old one as the compiler might need it”
        self installCompiledMethod.
        compiledMethod isRealPrimitive ifTrue: [ self generatePrimitiveWrapper ].
        self recompileAST.
        self installCompiledMethod.


2) compile a wrapper by building an AST by hand:

generatePrimitiveWrapper
        | wrappedMethod send wrapperMethod |
        ast compilationContext
                semanticAnalyzerClass: RFSemanticAnalyzer;
                astTranslatorClass: RFASTTranslator.
        ast doSemanticAnalysis. "force semantic analysis"
        wrappedMethod := ast generate: compiledMethod trailer.
       
        send := RBMessageNode
                receiver: (RBLiteralNode value: wrappedMethod)
                selector:  #valueWithReceiver:arguments:
                arguments: {RBVariableNode named: #self . RBArrayNode statements: ast variableNodes }.
       
        wrapperMethod := RBMethodNode
                selector: ast selector
                arguments: ast variableNodes
                body: (RBReturnNode value: send) asSequenceNode.
               
        wrapperMethod methodClass: ast methodClass.
        wrapperMethod propertyAt: #wrappedPrimitive put: true.
        ast hasMetalink ifTrue: [wrapperMethod propertyAt: #links put: (ast propertyAt: #links)].
        ast := wrapperMethod.

This means we copy over the links to the new wrapper. the #wrappedPrimitive marker is there so that,
when we destroy the twin when the last link is removed, we remember to unwrap.

3) the compiler plugin that takes the links into account ignores links on MethodNode if it’s a primitive.

4) when getting rid of the Twin (because we remove the last link), we detect if it was a primitive and unwrap:

destroyTwin
        (ast hasProperty: #wrappedPrimitive) ifTrue: [  ast :=  compiledMethod parseTree].
        self recompileAST.
        self installCompiledMethod.
        compiledMethod reflectiveMethod: nil.
        SystemAnnouncer uniqueInstance unsubscribe: self

e.g.

testBeforeMethodPrimitive
        | methodNode link |
        methodNode := (ReflectivityExamples >> #examplePrimitiveMethod) ast.
        link := MetaLink new
                metaObject: self;
                selector: #tagExec.
        self assert: (ReflectivityExamples>>#examplePrimitiveMethod) isRealPrimitive.
        methodNode link: link.
        self assert: methodNode hasMetalink.
        self assert: (ReflectivityExamples >> #examplePrimitiveMethod) class = CompiledMethod.
        self assert: tag isNil.
        self assert: ReflectivityExamples new examplePrimitiveMethod class = ByteString.
        self deny: (ReflectivityExamples>>#examplePrimitiveMethod) isRealPrimitive.
        self assert: tag = #yes.
        self assert: (ReflectivityExamples >> #examplePrimitiveMethod) class = CompiledMethod.
        link uninstall.
        self assert: (ReflectivityExamples>>#examplePrimitiveMethod) isRealPrimitive.
        ReflectivityExamples recompile: #examplePrimitiveMethod


Marcus