hpi-swa / RSqueak

A Squeak/Smalltalk VM written in RPython.
BSD 3-Clause "New" or "Revised" License
83 stars 15 forks source link

Generically allow primitives to return an error code #130

Open timfel opened 8 years ago

timfel commented 7 years ago

Basically, when a primitive fails there might be a "storeIntoTemp" bytecode directly after the primitive call, this will store an error code into the temp. We need to find a way to pass this error code into the method activation (or refactor how our primitive fails)

fniephaus commented 7 years ago

Let's get this done next week, so we can start providing error codes in primfail errors...

fniephaus commented 7 years ago

It is possible to expose an error code via a primitive pragma, e.g. <primitive: 128 error: ec>. We need to find out how the OSVM fills the ec temp var. However, PrimitiveFailedErrors used to contain error messages in RSqueak. But the generation of new strings turned out to be a performance overhead, especially in case of a frequently called primitive which is allowed to fail (these cases apparently exist). Postponing this again...

krono commented 7 years ago

Well, ec typically is a SmallInteger or a Symbol, so things that defy vm-level allocation.

fniephaus commented 7 years ago

But ec could also be a ByteString from the message defined in the PrimitiveFailedError("Some error message"). This would be very helpful especially when developing plugins and such.

krono commented 7 years ago

See

Smalltalk specialObjectsArray at: 52

and this from an old VMMaker:

ObjectMemory>>initializePrimitiveErrorCodes
    "Define the VM's primitive error codes.  N.B. these are
     replicated in platforms/Cross/vm/sqVirtualMachine.h."
    "ObjectMemory initializePrimitiveErrorCodes"
    | pet |
    PrimErrTableIndex := 51. "Zero-relative"
    "See SmalltalkImage>>recreateSpecialObjectsArray for the table definition.
     If the table exists and is large enough the corresponding entry is returned as
     the primitive error, otherwise the error is answered numerically."
    pet := Smalltalk specialObjectsArray at: PrimErrTableIndex + 1 ifAbsent: [#()].
    pet isArray ifFalse: [pet := #()].
    PrimNoErr := 0. "for helper methods that need to answer success or an error code."
    PrimErrGenericFailure   := pet indexOf: nil ifAbsent: 1.
    PrimErrBadReceiver      := pet indexOf: #'bad receiver' ifAbsent: 2.
    PrimErrBadArgument  := pet indexOf: #'bad argument' ifAbsent: 3.
    PrimErrBadIndex     := pet indexOf: #'bad index' ifAbsent: 4.
    PrimErrBadNumArgs   := pet indexOf: #'bad number of arguments' ifAbsent: 5.
    PrimErrInappropriate    := pet indexOf: #'inappropriate operation' ifAbsent: 6.
    PrimErrUnsupported  := pet indexOf: #'unsupported operation' ifAbsent: 7.
    PrimErrNoModification   := pet indexOf: #'no modification' ifAbsent: 8.
    PrimErrNoMemory     := pet indexOf: #'insufficient object memory' ifAbsent: 9.
    PrimErrNoCMemory        := pet indexOf: #'insufficient C memory' ifAbsent: 10.
    PrimErrNotFound     := pet indexOf: #'not found' ifAbsent: 11.
    PrimErrBadMethod        := pet indexOf: #'bad method' ifAbsent: 12.
    PrimErrNamedInternal    := pet indexOf: #'internal error in named primitive machinery' ifAbsent: 13.
    PrimErrObjectMayMove    := pet indexOf: #'object may move' ifAbsent: 14.
    PrimErrLimitExceeded    := pet indexOf: #'resource limit exceeded' ifAbsent: 15
krono commented 7 years ago

Also all senders of InterpreterPrimitives>>primitiveFailFor: have SmallIntegers as argument,

krono commented 7 years ago

Here's how StackInterpreter/Cog do it:

StackInterpreter>>justActivateNewMethod
      "..."
    "Pass primitive error code to last temp if method receives it (indicated by an
     initial long store temp bytecode).  We don't need to check that newMethod
     actually has a primitive because the initial 129 only occurs if there is one."
    primFailCode ~= 0 ifTrue:
        [(objectMemory byteAtPointer: instructionPointer + 1) = 129 "long store temp" ifTrue:
            [errorCode := self getErrorObjectFromPrimFailCode.
             self stackTopPut: errorCode "nil if primFailCode == 1, or primFailCode"].
        primFailCode := 0].

StackInterpreter>>getErrorObjectFromPrimFailCode
    "Answer the errorCode object to supply to a failing primitive method that accepts one.
     If there is a primitive error table and the primFailCode is a valid index there-in answer
     the coprresponding entry in the table, otherwise simply answer the code as an integer."
    | table |
    primFailCode > 0 ifTrue:
        [table := objectMemory splObj: PrimErrTableIndex.
         primFailCode <= ((objectMemory lastPointerOf: table) // self bytesPerWord) ifTrue:
            [^objectMemory fetchPointer: primFailCode - 1 ofObject: table]].
    ^objectMemory integerObjectOf: primFailCode