Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bir/bir_gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ func GenBir(ctx *context.CompilerContext, ast *ast.BLangPackage) *BIRPackage {
birPkg.MainFunction = birFunc
}
}
birPkg.TypeCtx = semtypes.ContextFrom(ctx.GetTypeEnv())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure this is a good idea. Type context is suppose to be for each thread not package. Each thread needs to create that threads type context using the type environment (which should be shared by all in the interpreter)

return birPkg
}

Expand Down
1 change: 1 addition & 0 deletions bir/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ type (
Functions []BIRFunction
Constants []BIRConstant
MainFunction *BIRFunction
TypeCtx semtypes.Context
}

BIRImportModule struct {
Expand Down
3 changes: 0 additions & 3 deletions corpus/integration_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 8 additions & 12 deletions runtime/internal/exec/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ import (
const maxRecursionDepth = 1000

func executeFunction(birFunc bir.BIRFunction, args []values.BalValue, reg *modules.Registry, callStack *callStack) values.BalValue {
funcKey := birFunc.FunctionLookupKey

localVars := &birFunc.LocalVars
locals := make([]values.BalValue, len(*localVars))
locals[0] = values.DefaultValueForType((*localVars)[0].Type)
Expand All @@ -37,30 +35,26 @@ func executeFunction(birFunc bir.BIRFunction, args []values.BalValue, reg *modul
for i := len(args) + 1; i < len(*localVars); i++ {
locals[i] = values.DefaultValueForType((*localVars)[i].Type)
}
frame := &Frame{locals: locals, functionKey: funcKey}
frame := &Frame{locals: locals, functionKey: birFunc.FunctionLookupKey}
callStack.Push(frame)
defer callStack.Pop()

if len(callStack.elements) > maxRecursionDepth {
panic("stack overflow")
}
bbs := birFunc.BasicBlocks
bb := &bbs[0]
bb := &birFunc.BasicBlocks[0]
for {
instructions := bb.Instructions
term := bb.Terminator
for _, instruction := range instructions {
execInstruction(instruction, frame)
for _, inst := range bb.Instructions {
execInstruction(inst, frame, reg)
}
bb = execTerminator(term, frame, reg, callStack)
bb = execTerminator(bb.Terminator, frame, reg, callStack)
if bb == nil {
break
}
}
return frame.locals[0]
}

func execInstruction(inst bir.BIRNonTerminator, frame *Frame) {
func execInstruction(inst bir.BIRNonTerminator, frame *Frame, reg *modules.Registry) {
switch v := inst.(type) {
case *bir.ConstantLoad:
execConstantLoad(v, frame)
Expand Down Expand Up @@ -145,6 +139,8 @@ func execInstruction(inst bir.BIRNonTerminator, frame *Frame) {
}
case *bir.TypeCast:
execTypeCast(v, frame)
case *bir.TypeTest:
execTypeTest(v, frame, reg)
default:
fmt.Printf("UNKNOWN_INSTRUCTION_TYPE(%T)\n", inst)
}
Expand Down
4 changes: 1 addition & 3 deletions runtime/internal/exec/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ func Interpret(pkg bir.BIRPackage, reg *modules.Registry) {
if pkg.MainFunction == nil {
panic("main function not found in BIR package")
}
callStack := &callStack{
elements: make([]*Frame, 0, 32),
}
callStack := &callStack{elements: make([]*Frame, 0, 32)}
executeFunction(*pkg.MainFunction, nil, reg, callStack)
}
12 changes: 10 additions & 2 deletions runtime/internal/exec/non_terminators.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package exec

import (
"ballerina-lang-go/bir"
"ballerina-lang-go/runtime/internal/modules"
"ballerina-lang-go/semtypes"
"ballerina-lang-go/values"
"fmt"
Expand Down Expand Up @@ -70,11 +71,18 @@ func execArrayLoad(access *bir.FieldAccess, frame *Frame) {

func execTypeCast(typeCast *bir.TypeCast, frame *Frame) {
sourceValue := frame.GetOperand(typeCast.RhsOp.Index)
targetType := typeCast.Type
result := castValue(sourceValue, targetType)
result := castValue(sourceValue, typeCast.Type)
frame.SetOperand(typeCast.LhsOp.Index, result)
}

func execTypeTest(typeTest *bir.TypeTest, frame *Frame, reg *modules.Registry) {
sourceValue := frame.GetOperand(typeTest.RhsOp.Index)
valueType := values.SemTypeForValue(sourceValue)
typeCtx := reg.GetTypeCtx()
matches := semtypes.IsSubtype(typeCtx, valueType, typeTest.Type) != typeTest.IsNegation
frame.SetOperand(typeTest.LhsOp.Index, matches)
}

func castValue(value values.BalValue, targetType semtypes.SemType) values.BalValue {
b, ok := targetType.(*semtypes.BasicTypeBitSet)
if !ok {
Expand Down
29 changes: 14 additions & 15 deletions runtime/internal/exec/terminators.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@

func execBranch(branchTerm *bir.Branch, frame *Frame) *bir.BIRBasicBlock {
opIndex := branchTerm.Op.Index
value := frame.GetOperand(opIndex)
cond, ok := value.(bool)
v := frame.GetOperand(opIndex)
cond, ok := v.(bool)
if !ok {
panic(fmt.Sprintf("invalid branch condition type at index %d: %T (expected bool)", opIndex, value))
panic(fmt.Sprintf("invalid branch condition type at index %d: %T (expected bool)", opIndex, v))

Check warning on line 31 in runtime/internal/exec/terminators.go

View check run for this annotation

Codecov / codecov/patch

runtime/internal/exec/terminators.go#L31

Added line #L31 was not covered by tests
}
if cond {
return branchTerm.TrueBB
Expand All @@ -37,39 +37,38 @@
}

func execCall(callInfo *bir.Call, frame *Frame, reg *modules.Registry, callStack *callStack) *bir.BIRBasicBlock {
values := extractArgs(callInfo.Args, frame)
result := executeCall(callInfo, values, reg, callStack)
args := extractArgs(callInfo.Args, frame)
result := executeCall(callInfo, args, reg, callStack)
if callInfo.LhsOp != nil {
frame.SetOperand(callInfo.LhsOp.Index, result)
}
return callInfo.ThenBB
}

func executeCall(callInfo *bir.Call, values []values.BalValue, reg *modules.Registry, callStack *callStack) values.BalValue {
func executeCall(callInfo *bir.Call, args []values.BalValue, reg *modules.Registry, callStack *callStack) values.BalValue {
if callInfo.CachedBIRFunc != nil {
return executeFunction(*callInfo.CachedBIRFunc, values, reg, callStack)
return executeFunction(*callInfo.CachedBIRFunc, args, reg, callStack)
}
if callInfo.CachedNativeFunc != nil {
result, err := callInfo.CachedNativeFunc(values)
result, err := callInfo.CachedNativeFunc(args)
if err != nil {
panic(err)
}
return result
}
return lookupAndExecute(callInfo, values, reg, callStack)
return lookupAndExecute(callInfo, args, reg, callStack)
}

func lookupAndExecute(callInfo *bir.Call, values []values.BalValue, reg *modules.Registry, callStack *callStack) values.BalValue {
lookupKey := callInfo.FunctionLookupKey
fn := reg.GetBIRFunction(lookupKey)
func lookupAndExecute(callInfo *bir.Call, args []values.BalValue, reg *modules.Registry, callStack *callStack) values.BalValue {
fn := reg.GetBIRFunction(callInfo.FunctionLookupKey)
if fn != nil {
callInfo.CachedBIRFunc = fn
return executeFunction(*fn, values, reg, callStack)
return executeFunction(*fn, args, reg, callStack)
}
externFn := reg.GetNativeFunction(lookupKey)
externFn := reg.GetNativeFunction(callInfo.FunctionLookupKey)
if externFn != nil {
callInfo.CachedNativeFunc = externFn.Impl
result, err := externFn.Impl(values)
result, err := externFn.Impl(args)
if err != nil {
panic(err)
}
Expand Down
10 changes: 10 additions & 0 deletions runtime/internal/modules/registry_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ package modules
import (
"ballerina-lang-go/bir"
"ballerina-lang-go/model"
"ballerina-lang-go/semtypes"
"ballerina-lang-go/values"
)

type Registry struct {
birFunctions map[string]*bir.BIRFunction
nativeFunctions map[string]*ExternFunction
typeCtx semtypes.Context
}

func NewRegistry() *Registry {
Expand All @@ -44,6 +46,14 @@ func (r *Registry) RegisterModule(id *model.PackageID, m *BIRModule) *BIRModule
return m
}

func (r *Registry) SetTypeCtx(cx semtypes.Context) {
r.typeCtx = cx
}

func (r *Registry) GetTypeCtx() semtypes.Context {
return r.typeCtx
}

func (r *Registry) RegisterExternFunction(orgName string, moduleName string, funcName string, impl func(args []values.BalValue) (values.BalValue, error)) {
externFn := &ExternFunction{
Name: funcName,
Expand Down
1 change: 1 addition & 0 deletions runtime/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ func (rt *Runtime) Interpret(pkg bir.BIRPackage) (err error) {
}
}
}()
rt.registry.SetTypeCtx(pkg.TypeCtx)
exec.Interpret(pkg, rt.registry)
return err
}
Expand Down
21 changes: 21 additions & 0 deletions values/values.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,24 @@
return NeverValue
}
}

func SemTypeForValue(v BalValue) semtypes.SemType {
Copy link
Member

@heshanpadmasiri heshanpadmasiri Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is going to work for simple basic types you need the singleton basic type otherwise something like this wont work

int a = 5;
io:println(a is byte)

switch v := v.(type) {
case nil:
return &semtypes.NIL
case bool:
return &semtypes.BOOLEAN

Check warning on line 59 in values/values.go

View check run for this annotation

Codecov / codecov/patch

values/values.go#L56-L59

Added lines #L56 - L59 were not covered by tests
case int64:
return &semtypes.INT
case float64:
return &semtypes.FLOAT
case string:
return &semtypes.STRING
case *big.Rat:
return &semtypes.DECIMAL

Check warning on line 67 in values/values.go

View check run for this annotation

Codecov / codecov/patch

values/values.go#L62-L67

Added lines #L62 - L67 were not covered by tests
case *List:
return v.Type
default:
return &semtypes.ANY

Check warning on line 71 in values/values.go

View check run for this annotation

Codecov / codecov/patch

values/values.go#L70-L71

Added lines #L70 - L71 were not covered by tests
}
}