Skip to content

Commit 27c6618

Browse files
author
mattcce
committed
method declaration and invoke handling
1 parent 846ee0d commit 27c6618

File tree

7 files changed

+188
-92
lines changed

7 files changed

+188
-92
lines changed
Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,51 @@
1-
import { Environment } from "../../components";
2-
import { STEP_LIMIT } from "../../constants";
3-
import { ControlItem, Context, StashItem, StructType } from "../../types";
4-
import { Stack, isNode } from "../../utils";
1+
import { Environment } from '../../components'
2+
import { STEP_LIMIT } from '../../constants'
3+
import { ControlItem, Context, StashItem, StructType } from '../../types'
4+
import { Stack, isNode } from '../../utils'
55

66
export class StackStub<T> extends Stack<T> {
7-
private trace: T[] = [];
7+
private trace: T[] = []
88

99
public push(...items: T[]): void {
1010
for (const item of items) {
11-
super.push(item);
12-
this.trace.push(item);
11+
super.push(item)
12+
this.trace.push(item)
1313
}
14-
};
14+
}
1515

1616
public getTrace(): T[] {
17-
return this.trace;
18-
};
19-
};
20-
export class ControlStub extends StackStub<ControlItem> {};
21-
export class StashStub extends StackStub<StashItem> {};
17+
return this.trace
18+
}
19+
}
20+
export class ControlStub extends StackStub<ControlItem> {}
21+
export class StashStub extends StackStub<StashItem> {}
2222
// TODO make env traceable
23-
export class EnvironmentStub extends Environment {};
23+
export class EnvironmentStub extends Environment {}
2424

2525
export const createContextStub = (): Context => ({
2626
errors: [],
2727
control: new ControlStub(),
2828
stash: new StashStub(),
2929
environment: new EnvironmentStub(),
30-
totalSteps: STEP_LIMIT,
31-
});
30+
totalSteps: STEP_LIMIT
31+
})
3232

3333
export const getControlItemStr = (i: ControlItem): string => {
34-
return isNode(i) ? i.kind : i.instrType;
35-
};
34+
return isNode(i) ? i.kind : i.instrType
35+
}
3636

3737
export const getStashItemStr = (i: StashItem): string => {
38-
return i.kind === "Literal"
39-
? i.literalType.value
38+
return i.kind === 'Literal'
39+
? i.literalType.value
4040
: i.kind === StructType.CLOSURE
41-
? i.mtdOrCon.kind === "MethodDeclaration"
42-
? i.mtdOrCon.methodHeader.identifier
43-
: i.mtdOrCon.constructorDeclarator.identifier
44-
: i.kind === StructType.VARIABLE
45-
? i.name
46-
: i.kind === StructType.CLASS
47-
? i.frame.name
48-
: i.kind === StructType.TYPE
49-
? i.type
50-
: i.kind;
51-
};
41+
? i.decl.kind === 'MethodDeclaration' || i.decl.kind === 'NativeDeclaration'
42+
? i.decl.methodHeader.identifier
43+
: i.decl.constructorDeclarator.identifier
44+
: i.kind === StructType.VARIABLE
45+
? i.name
46+
: i.kind === StructType.CLASS
47+
? i.frame.name
48+
: i.kind === StructType.TYPE
49+
? i.type
50+
: i.kind
51+
}

src/ec-evaluator/__tests__/natives.test.ts

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { natives } from '../natives'
1+
import { foreigns } from '../natives'
22
import {
33
// ControlStub,
44
// StashStub,
@@ -10,10 +10,38 @@ import { parse } from '../../ast/parser'
1010
import { evaluate } from '../interpreter'
1111

1212
describe('native functions', () => {
13-
it('should invoke external native function', () => {
13+
it('method declaration should dynamically link foreign function', () => {
1414
const mockForeignFn = jest.fn()
1515

16-
natives['testNative(): void'] = mockForeignFn
16+
foreigns['C::testNative(): void'] = mockForeignFn
17+
18+
const programStr = `
19+
class C {
20+
public native void testNative();
21+
22+
public static void main(String[] args) {
23+
C c = new C();
24+
c.testNative();
25+
}
26+
}
27+
`
28+
29+
const compilationUnit = parse(programStr)
30+
expect(compilationUnit).toBeTruthy()
31+
32+
const context = createContextStub()
33+
context.control.push(compilationUnit!)
34+
35+
evaluate(context)
36+
37+
const c = context.environment.getClass('C')
38+
c.instanceMethods.find(md => md)
39+
})
40+
41+
it('invoke instruction should invoke external native function', () => {
42+
const mockForeignFn = jest.fn()
43+
44+
foreigns['C::testNative(): void'] = mockForeignFn
1745

1846
const programStr = `
1947
class C {
@@ -42,7 +70,7 @@ describe('native functions', () => {
4270
expect(s).toBe('"Test"')
4371
})
4472

45-
natives['testNative(String s): void'] = foreignFn
73+
foreigns['C::testNative(String s): void'] = foreignFn
4674

4775
const programStr = `
4876
class C {

src/ec-evaluator/interpreter.ts

Lines changed: 45 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ import {
6565
ResConOverloadInstr,
6666
ResOverrideInstr,
6767
ResTypeContInstr,
68-
StructType
68+
StructType,
69+
NativeDeclaration
6970
} from './types'
7071
import {
7172
defaultValues,
@@ -87,15 +88,15 @@ import {
8788
searchMainMtdClass,
8889
prependExpConInvIfNeeded,
8990
isStatic,
90-
isNative,
91+
isNativeMethodDeclaration,
9192
resOverload,
9293
resOverride,
9394
resConOverload,
9495
isNull,
9596
makeNonLocalVarNonParamSimpleNameQualified,
9697
getFullyQualifiedDescriptor
9798
} from './utils'
98-
import { natives } from './natives'
99+
import { foreigns } from './natives'
99100

100101
type CmdEvaluator = (
101102
command: ControlItem,
@@ -253,9 +254,31 @@ export const cmdEvaluators: { [type: string]: CmdEvaluator } = {
253254
_control: Control,
254255
_stash: Stash
255256
) => {
257+
let mtdClosure
258+
// Native method handling
259+
if (isNativeMethodDeclaration(command)) {
260+
const FQMtdDesc = getFullyQualifiedDescriptor(environment.current.name, command)
261+
const fn = foreigns[FQMtdDesc]
262+
263+
if (fn === undefined) {
264+
throw new errors.UndefinedNativeMethod(FQMtdDesc)
265+
}
266+
267+
// bind foreign function
268+
const natDecl: NativeDeclaration = {
269+
kind: 'NativeDeclaration',
270+
methodModifier: command.methodModifier,
271+
methodHeader: command.methodHeader,
272+
foreignFunction: fn
273+
}
274+
275+
mtdClosure = struct.closureStruct(natDecl, environment.current)
276+
} else {
277+
mtdClosure = struct.closureStruct(command, environment.current)
278+
}
279+
256280
// Use method descriptor as key.
257281
const mtdDescriptor: string = getDescriptor(command)
258-
const mtdClosure = struct.closureStruct(command, environment.current)
259282
environment.defineMtdOrCon(mtdDescriptor, mtdClosure)
260283
},
261284

@@ -471,13 +494,13 @@ export const cmdEvaluators: { [type: string]: CmdEvaluator } = {
471494
const closure: Closure = stash.pop()! as Closure
472495

473496
const params: FormalParameter[] =
474-
closure.mtdOrCon.kind === 'MethodDeclaration'
475-
? cloneDeep(closure.mtdOrCon.methodHeader.formalParameterList)
476-
: cloneDeep(closure.mtdOrCon.constructorDeclarator.formalParameterList)
497+
closure.decl.kind === 'MethodDeclaration' || closure.decl.kind === 'NativeDeclaration'
498+
? cloneDeep(closure.decl.methodHeader.formalParameterList)
499+
: cloneDeep(closure.decl.constructorDeclarator.formalParameterList)
477500

478501
// Extend env from global frame.
479-
const mtdOrConDescriptor = getDescriptor(closure.mtdOrCon)
480-
environment.extendEnv(environment.global, mtdOrConDescriptor)
502+
const methodDescriptor = getDescriptor(closure.decl)
503+
environment.extendEnv(environment.global, methodDescriptor)
481504

482505
const isInstanceMtdOrCon = args.length == params.length + 1
483506
if (isInstanceMtdOrCon) {
@@ -504,17 +527,10 @@ export const cmdEvaluators: { [type: string]: CmdEvaluator } = {
504527
environment.defineVariable(params[i].identifier, params[i].unannType, args[i])
505528
}
506529

507-
// Native function escape hatch
508-
if (closure.mtdOrCon.kind === 'MethodDeclaration' && isNative(closure.mtdOrCon)) {
509-
const nativeFnDescriptor = getFullyQualifiedDescriptor(closure.mtdOrCon)
510-
const nativeFn = natives[nativeFnDescriptor]
511-
512-
if (!nativeFn) {
513-
throw new errors.UndefinedNativeMethod(nativeFnDescriptor)
514-
}
515-
516-
// call foreign fn
517-
nativeFn({ control, stash, environment })
530+
// Native functions
531+
if (closure.decl.kind === 'NativeDeclaration') {
532+
// call foreign fn with CSE machine state
533+
closure.decl.foreignFunction({ control, stash, environment })
518534

519535
// only because resetInstr demands one, never actually used
520536
const superfluousReturnStatement: ReturnStatement = {
@@ -524,15 +540,14 @@ export const cmdEvaluators: { [type: string]: CmdEvaluator } = {
524540

525541
// handle return from native fn
526542
control.push(instr.resetInstr(superfluousReturnStatement))
527-
return
543+
} else {
544+
// Push method/constructor body.
545+
const body =
546+
closure.decl.kind === 'MethodDeclaration'
547+
? closure.decl.methodBody
548+
: closure.decl.constructorBody
549+
control.push(body)
528550
}
529-
530-
// Push method/constructor body.
531-
const body =
532-
closure.mtdOrCon.kind === 'MethodDeclaration'
533-
? closure.mtdOrCon.methodBody
534-
: closure.mtdOrCon.constructorBody
535-
control.push(body)
536551
},
537552

538553
[InstrType.ENV]: (
@@ -811,7 +826,7 @@ export const cmdEvaluators: { [type: string]: CmdEvaluator } = {
811826
stash.push(closure)
812827

813828
// Post-processing required if overload resolved method is instance method.
814-
if (isInstance(closure.mtdOrCon as MethodDeclaration)) {
829+
if (isInstance(closure.decl as MethodDeclaration)) {
815830
// Increment arity of InvInstr on control.
816831
let n = 1
817832
while (
@@ -834,7 +849,7 @@ export const cmdEvaluators: { [type: string]: CmdEvaluator } = {
834849
const target = stash.pop()!
835850
const overloadResolvedClosure = stash.pop()! as Closure
836851

837-
if (isStatic(overloadResolvedClosure.mtdOrCon as MethodDeclaration)) {
852+
if (isStatic(overloadResolvedClosure.decl as MethodDeclaration)) {
838853
// No method overriding resolution is required if resolved method is a static method.
839854
stash.push(overloadResolvedClosure)
840855
return

src/ec-evaluator/natives.ts

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Control, Environment, Stash } from './components'
2+
import { StashItem } from './types'
23

34
/*
45
Native function escape hatch.
@@ -14,7 +15,7 @@ import { Control, Environment, Stash } from './components'
1415
The current implementation automatically injects a return instruction after the external handler function call ends.
1516
*/
1617

17-
export type NativeFunction = ({
18+
export type ForeignFunction = ({
1819
control,
1920
stash,
2021
environment
@@ -24,6 +25,25 @@ export type NativeFunction = ({
2425
environment: Environment
2526
}) => void
2627

27-
export const natives: {
28-
[descriptor: string]: NativeFunction
29-
} = {}
28+
export const foreigns: {
29+
[descriptor: string]: ForeignFunction
30+
} = {
31+
'Object::hashCode(): int': ({ stash }) => {
32+
const hashCode = Math.floor(Math.random() * Math.pow(2, 32))
33+
const stashItem: StashItem = {
34+
kind: 'Literal',
35+
literalType: { kind: 'DecimalIntegerLiteral', value: String(hashCode) }
36+
}
37+
38+
console.log(stashItem)
39+
stash.push(stashItem)
40+
},
41+
42+
'Object::display(String s): void': ({ environment }) => {
43+
// @ts-expect-error ts(2339): guaranteed valid by type checker
44+
const s = environment.getVariable('s').value.literalType.value
45+
46+
// TODO: hook up to frontend
47+
console.log(s)
48+
}
49+
}

src/ec-evaluator/structCreator.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,18 @@ import {
66
UnannType
77
} from '../ast/types/classes'
88
import { EnvNode } from './components'
9-
import { Name, StructType, Type, VarValue, Variable, Symbol, Object, Class, Closure } from './types'
9+
import {
10+
Name,
11+
StructType,
12+
Type,
13+
VarValue,
14+
Variable,
15+
Symbol,
16+
Object,
17+
Class,
18+
Closure,
19+
NativeDeclaration
20+
} from './types'
1021

1122
export const varStruct = (type: UnannType, name: Name, value: VarValue): Variable => ({
1223
kind: StructType.VARIABLE,
@@ -27,11 +38,11 @@ export const objStruct = (frame: EnvNode, c: Class): Object => ({
2738
})
2839

2940
export const closureStruct = (
30-
mtdOrCon: MethodDeclaration | ConstructorDeclaration,
41+
decl: MethodDeclaration | ConstructorDeclaration | NativeDeclaration,
3142
env: EnvNode
3243
): Closure => ({
3344
kind: StructType.CLOSURE,
34-
mtdOrCon,
45+
decl,
3546
env
3647
})
3748

0 commit comments

Comments
 (0)