@@ -412,13 +412,13 @@ export class Compiler extends DiagnosticEmitter {
412
412
if ( options . isWasm64 ) {
413
413
module . addGlobal ( BuiltinNames . heap_base , NativeType . I64 , true , module . i64 ( 0 ) ) ;
414
414
module . addGlobal ( BuiltinNames . rtti_base , NativeType . I64 , true , module . i64 ( 0 ) ) ;
415
+ module . addGlobal ( BuiltinNames . global_closure , NativeType . I64 , true , module . i64 ( - 1 ) ) ;
415
416
} else {
416
417
module . addGlobal ( BuiltinNames . heap_base , NativeType . I32 , true , module . i32 ( 0 ) ) ;
417
418
module . addGlobal ( BuiltinNames . rtti_base , NativeType . I32 , true , module . i32 ( 0 ) ) ;
419
+ module . addGlobal ( BuiltinNames . global_closure , NativeType . I32 , true , module . i32 ( - 1 ) ) ;
418
420
}
419
421
420
- module . addGlobal ( BuiltinNames . global_closure , NativeType . I32 , true , module . i32 ( - 1 ) ) ;
421
-
422
422
// compile entry file(s) while traversing reachable elements
423
423
var files = program . filesByName ;
424
424
// TODO: for (let file of files.values()) {
@@ -1826,6 +1826,10 @@ export class Compiler extends DiagnosticEmitter {
1826
1826
var functionTable = this . functionTable ;
1827
1827
var tableBase = this . options . tableBase ;
1828
1828
if ( ! tableBase ) tableBase = 1 ; // leave first elem blank
1829
+ // Skip every 16th entry, which is resevered for closures
1830
+ if ( tableBase + functionTable . length % 16 == 0 ) {
1831
+ functionTable . push ( "" )
1832
+ }
1829
1833
index = tableBase + functionTable . length ;
1830
1834
if ( ! instance . is ( CommonFlags . TRAMPOLINE ) && instance . signature . requiredParameters < instance . signature . parameterTypes . length ) {
1831
1835
// insert the trampoline if the function has optional parameters
@@ -3411,7 +3415,7 @@ export class Compiler extends DiagnosticEmitter {
3411
3415
// pretend to retain the expression immediately so the autorelease, if any, is skipped
3412
3416
var expr = this . compileExpression ( expression , returnType , constraints | Constraints . WILL_RETAIN ) ;
3413
3417
var flow = this . currentFlow ;
3414
- if ( returnType . isManaged || returnType . signatureReference ) {
3418
+ if ( returnType . isManaged ) {
3415
3419
// check if that worked, and if it didn't, keep the reference alive
3416
3420
if ( ! this . skippedAutoreleases . has ( expr ) ) {
3417
3421
let index = this . tryUndoAutorelease ( expr , flow ) ;
@@ -3464,8 +3468,17 @@ export class Compiler extends DiagnosticEmitter {
3464
3468
}
3465
3469
fromType = fromType . nonNullableType ;
3466
3470
}
3471
+ // When we convert from the closure type into a function pointer, we first
3472
+ // update the local copy of the scope with the newest values
3467
3473
if ( fromType . is ( TypeFlags . CLOSURE ) ) {
3468
- return module . block ( null , [ this . injectClosedLocals ( expr , fromType ) , expr ] , NativeType . I32 )
3474
+ //Retain the Closure Class memory so it isn't freed when we change it into a function ptr
3475
+ if ( ! this . skippedAutoreleases . has ( expr ) ) {
3476
+ let index = this . tryUndoAutorelease ( expr , this . currentFlow ) ;
3477
+ if ( index == - 1 ) expr = this . makeRetain ( expr ) ;
3478
+ this . skippedAutoreleases . add ( expr ) ;
3479
+ }
3480
+ // TODO: check first to ensure that signatures are compatible
3481
+ return module . block ( null , [ this . injectClosedLocals ( expr , fromType ) , expr ] , toType . toNativeType ( ) )
3469
3482
}
3470
3483
if ( fromType . isAssignableTo ( toType ) ) { // downcast or same
3471
3484
assert ( fromType . kind == toType . kind ) ;
@@ -5803,15 +5816,12 @@ export class Compiler extends DiagnosticEmitter {
5803
5816
// fall-through
5804
5817
}
5805
5818
case ElementKind . LOCAL :
5819
+ case ElementKind . CLOSEDLOCAL :
5806
5820
case ElementKind . FIELD : {
5807
5821
targetType = ( < VariableLikeElement > target ) . type ;
5808
5822
if ( target . hasDecorator ( DecoratorFlags . UNSAFE ) ) this . checkUnsafe ( expression ) ;
5809
5823
break ;
5810
5824
}
5811
- case ElementKind . CLOSEDLOCAL : {
5812
- targetType = Type . i32
5813
- break ;
5814
- }
5815
5825
case ElementKind . PROPERTY_PROTOTYPE : { // static property
5816
5826
let propertyPrototype = < PropertyPrototype > target ;
5817
5827
let setterPrototype = propertyPrototype . setterPrototype ;
@@ -5937,21 +5947,12 @@ export class Compiler extends DiagnosticEmitter {
5937
5947
return this . makeLocalAssignment ( local , valueExpr , valueType , tee ) ;
5938
5948
}
5939
5949
case ElementKind . CLOSEDLOCAL : {
5950
+ // TODO: ability to update closed over locals
5940
5951
this . error (
5941
5952
DiagnosticCode . Not_implemented ,
5942
5953
valueExpression . range
5943
5954
) ;
5944
5955
return module . unreachable ( ) ;
5945
- //return module.block(null, [
5946
- //module.store(
5947
- //1,
5948
- //this.module.global_get(BuiltinNames.global_closure, this.options.nativeSizeType),
5949
- //valueExpr,
5950
- //NativeType.I32,
5951
- //4
5952
- //),
5953
- //valueExpr
5954
- //], NativeType.I32)
5955
5956
}
5956
5957
case ElementKind . GLOBAL : {
5957
5958
let global = < Global > target ;
@@ -6332,18 +6333,19 @@ export class Compiler extends DiagnosticEmitter {
6332
6333
) : ExpressionRef {
6333
6334
var module = this . module ;
6334
6335
var locals = type . locals ! ;
6335
- var exprs = new Array < ExpressionRef > ( ) ;
6336
- for ( let _values = Map_values ( locals ) , i = 0 , k = _values . length ; i < k ; ++ i ) {
6336
+ let _values = Map_values ( locals ) ;
6337
+ var exprs = new Array < ExpressionRef > ( _values . length ) ;
6338
+ for ( let i = 0 , k = _values . length ; i < k ; ++ i ) {
6337
6339
let local = unchecked ( _values [ i ] ) ;
6338
- exprs . push ( module . store (
6340
+ let closureClass = type . classReference !
6341
+ exprs [ i ] = module . store (
6339
6342
local . type . byteSize ,
6340
6343
expr ,
6341
- this . module . local_get ( local . index , NativeType . I32 ) ,
6342
- NativeType . I32 ,
6343
- ( i + 1 ) * 4
6344
- ) )
6344
+ this . module . local_get ( local . index , local . type . toNativeType ( ) ) ,
6345
+ local . type . toNativeType ( ) ,
6346
+ closureClass . offsetof ( local . name )
6347
+ )
6345
6348
}
6346
- //exprs.push(module.unreachable())
6347
6349
return module . block ( null , exprs )
6348
6350
}
6349
6351
@@ -6424,7 +6426,6 @@ export class Compiler extends DiagnosticEmitter {
6424
6426
6425
6427
var signature : Signature | null ;
6426
6428
var indexArg : ExpressionRef ;
6427
- var closedLocal : Local | null = null ;
6428
6429
switch ( target . kind ) {
6429
6430
// direct call: concrete function
6430
6431
case ElementKind . FUNCTION_PROTOTYPE : {
@@ -6451,36 +6452,34 @@ export class Compiler extends DiagnosticEmitter {
6451
6452
// indirect call: index argument with signature (non-generic, can't be inlined)
6452
6453
case ElementKind . LOCAL : {
6453
6454
let local = < Local > target ;
6455
+ signature = local . type . signatureReference ;
6454
6456
if ( local . type . is ( TypeFlags . CLOSURE ) ) {
6455
- signature = new Signature ( this . program , [ ] , Type . i32 )
6456
- flow . locals = local . type . locals ! ;
6457
-
6458
6457
indexArg = module . block ( null , [
6459
6458
this . injectClosedLocals (
6460
6459
this . module . local_get ( local . index , this . options . nativeSizeType ) ,
6461
6460
local . type
6462
6461
) ,
6463
6462
module . global_set (
6464
6463
BuiltinNames . global_closure ,
6465
- this . module . local_get ( local . index , NativeType . I32 )
6464
+ this . module . local_get ( local . index , this . options . nativeSizeType )
6466
6465
) ,
6467
6466
module . load (
6468
- 4 ,
6467
+ local . type . byteSize ,
6469
6468
local . type . is ( TypeFlags . SIGNED ) ,
6470
- this . module . local_get ( local . index , NativeType . I32 ) ,
6471
- NativeType . I32 ,
6469
+ this . module . local_get ( local . index , this . options . nativeSizeType ) ,
6470
+ this . options . nativeSizeType ,
6472
6471
0
6473
6472
)
6474
- ] , NativeType . I32 ) ;
6473
+ ] , this . options . nativeSizeType ) ;
6475
6474
break ;
6476
6475
}
6477
- signature = local . type . signatureReference ;
6478
6476
if ( signature ) {
6479
6477
if ( local . is ( CommonFlags . INLINED ) ) {
6480
6478
indexArg = module . i32 ( i64_low ( local . constantIntegerValue ) ) ;
6481
6479
} else {
6482
6480
indexArg = module . local_get ( local . index , NativeType . I32 ) ;
6483
6481
}
6482
+ // If the 16 is divisible by 16, then it must be a closure, so we call it as such
6484
6483
indexArg = module . if (
6485
6484
module . binary (
6486
6485
BinaryOp . EqI32 ,
@@ -6496,14 +6495,14 @@ export class Compiler extends DiagnosticEmitter {
6496
6495
BuiltinNames . global_closure ,
6497
6496
indexArg
6498
6497
) ,
6499
- module . load (
6498
+ module . load ( //TODO: support 8 byte addrs
6500
6499
4 ,
6501
6500
true ,
6502
6501
indexArg ,
6503
6502
NativeType . I32 ,
6504
6503
0
6505
6504
)
6506
- ] , NativeType . I32 ) ,
6505
+ ] , this . options . nativeSizeType ) ,
6507
6506
indexArg
6508
6507
)
6509
6508
break ;
@@ -6607,32 +6606,15 @@ export class Compiler extends DiagnosticEmitter {
6607
6606
return module . unreachable ( ) ;
6608
6607
}
6609
6608
}
6610
- var callExpr = this . compileCallIndirect (
6609
+
6610
+ return this . compileCallIndirect (
6611
6611
assert ( signature ) , // FIXME: asc can't see this yet
6612
6612
indexArg ,
6613
6613
expression . arguments ,
6614
6614
expression ,
6615
6615
0 ,
6616
6616
contextualType == Type . void
6617
6617
) ;
6618
-
6619
- //Closure write-back
6620
- //if (closedLocal) {
6621
- //return module.block(null, [
6622
- //callExpr,
6623
- //module.local_set(
6624
- //closedLocal.index,
6625
- //module.load(
6626
- //1,
6627
- //true,
6628
- //this.module.global_get(BuiltinNames.global_closure, NativeType.I32),
6629
- //NativeType.I32,
6630
- //4
6631
- //)
6632
- //)
6633
- //], NativeType.I32)
6634
- //}
6635
- return callExpr ;
6636
6618
}
6637
6619
6638
6620
private compileCallExpressionBuiltin (
@@ -7129,7 +7111,6 @@ export class Compiler extends DiagnosticEmitter {
7129
7111
var module = this . module ;
7130
7112
var flow = this . currentFlow ;
7131
7113
var nativeSizeType = this . options . nativeSizeType ;
7132
- assert ( false )
7133
7114
if ( alreadyRetained ) {
7134
7115
// (t1=newExpr), __release(oldExpr), t1
7135
7116
// it is important that `newExpr` evaluates before `oldExpr` is released, hence the local
@@ -7196,7 +7177,6 @@ export class Compiler extends DiagnosticEmitter {
7196
7177
//
7197
7178
// callReceivingAReference((__release(t = callReturningAReference()), t))
7198
7179
//
7199
- assert ( false )
7200
7180
var local = flow . getAutoreleaseLocal ( type ) ;
7201
7181
if ( flow . isNonnull ( expr , type ) ) flow . setLocalFlag ( local . index , LocalFlags . NONNULL ) ;
7202
7182
return this . module . local_tee ( local . index , expr ) ;
@@ -7723,8 +7703,8 @@ export class Compiler extends DiagnosticEmitter {
7723
7703
var instance : Function | null ;
7724
7704
var contextualTypeArguments = makeMap ( flow . contextualTypeArguments ) ;
7725
7705
7726
- // compile according to context. this differs from a normal function in that omitted parameter
7727
- // and return types can be inferred and omitted arguments can be replaced with dummies.
7706
+ // compile according to context. this differs from a normal function in that omitted parameter
7707
+ // and return types can be inferred and omitted arguments can be replaced with dummies.
7728
7708
if ( contextualSignature ) {
7729
7709
let signatureNode = prototype . functionTypeNode ;
7730
7710
let parameterNodes = signatureNode . parameters ;
@@ -7833,28 +7813,36 @@ export class Compiler extends DiagnosticEmitter {
7833
7813
}
7834
7814
7835
7815
var index = this . ensureFunctionTableEntry ( instance ) ; // reports
7836
- console . log ( "function index" + index )
7837
7816
7838
7817
if ( index < 0 ) return this . module . unreachable ( ) ;
7839
7818
7840
7819
if ( instance . closedLocals . size > 0 ) {
7820
+ //Create field declarations for the function and each closed local
7841
7821
var members = Array < DeclarationStatement > ( instance . closedLocals . size + 1 ) ;
7842
7822
members [ 0 ] = this . program . makeNativeMember ( "__functionPtr" , "u32" )
7843
7823
for ( let _values = Map_values ( instance . closedLocals ) , i = 0 , k = _values . length ; i < k ; ++ i ) {
7844
7824
let local = unchecked ( _values [ i ] ) ;
7845
- members [ i + 1 ] = this . program . makeNativeMember ( local . name , "i32" ) //todo, support non-i32
7825
+ members [ i + 1 ] = this . program . makeNativeMember ( local . name , local . type . intType . toString ( ) )
7846
7826
}
7847
7827
7848
- var closureClassPrototype = this . program . makeNativeClassPrototype (
7849
- "closure|" + actualFunction . nextAnonymousId . toString ( ) ,
7828
+ //Create a native class prototype with a dummy syntax tree, similar to native functions
7829
+ var closureClassPrototype = assert ( this . program . makeNativeClassPrototype (
7830
+ "closure|" + this . program . nextClassId . toString ( ) ,
7850
7831
members
7851
- ) ! ;
7832
+ ) ) ;
7833
+
7834
+ //Resolve this prototype to get the class
7852
7835
var closureClass = this . resolver . resolveClass ( closureClassPrototype , null ) ! ;
7836
+
7837
+ //Compile this class to get the type
7853
7838
this . compileClass ( closureClass ) ;
7854
- //set the current type to be the generated type
7855
- this . currentType = closureClass . type . asClosure ( instance . signature ) //generatedClosureType;
7856
- //create local to hold closure
7839
+
7840
+ //Append the appropriate signature and flags for this closure type, then set it to currentType
7841
+ this . currentType = closureClass . type . asClosure ( instance . signature )
7842
+
7843
+ //create a local which will hold our closure class instance
7857
7844
var tempLocal = flow . getTempLocal ( this . currentType ) ;
7845
+
7858
7846
//copied closed locals into type
7859
7847
this . currentType . locals = instance . closedLocals ;
7860
7848
@@ -8125,13 +8113,19 @@ export class Compiler extends DiagnosticEmitter {
8125
8113
case ElementKind . CLOSEDLOCAL : {
8126
8114
let closedLocal = < ClosedLocal > target ;
8127
8115
8128
- return module . load (
8129
- 4 ,
8116
+ // TODO: replace this with a class field access, once we are able to construct the class before
8117
+ // compiling
8118
+ let loadExpr = module . load (
8119
+ closedLocal . type . byteSize ,
8130
8120
true ,
8131
8121
this . module . global_get ( BuiltinNames . global_closure , NativeType . I32 ) ,
8132
- NativeType . I32 ,
8133
- flow . actualFunction . closedLocals . size * 4
8122
+ closedLocal . type . toNativeType ( ) ,
8123
+ flow . actualFunction . nextGlobalClosureOffset
8134
8124
) ;
8125
+
8126
+ flow . actualFunction . nextGlobalClosureOffset += closedLocal . type . byteSize ;
8127
+
8128
+ return loadExpr
8135
8129
}
8136
8130
}
8137
8131
this . error (
@@ -9452,7 +9446,6 @@ export class Compiler extends DiagnosticEmitter {
9452
9446
break ;
9453
9447
}
9454
9448
default : {
9455
- assert ( false ) ;
9456
9449
return module . unreachable ( ) ;
9457
9450
}
9458
9451
}
0 commit comments