@@ -412,13 +412,13 @@ export class Compiler extends DiagnosticEmitter {
412412 if ( options . isWasm64 ) {
413413 module . addGlobal ( BuiltinNames . heap_base , NativeType . I64 , true , module . i64 ( 0 ) ) ;
414414 module . addGlobal ( BuiltinNames . rtti_base , NativeType . I64 , true , module . i64 ( 0 ) ) ;
415+ module . addGlobal ( BuiltinNames . global_closure , NativeType . I64 , true , module . i64 ( - 1 ) ) ;
415416 } else {
416417 module . addGlobal ( BuiltinNames . heap_base , NativeType . I32 , true , module . i32 ( 0 ) ) ;
417418 module . addGlobal ( BuiltinNames . rtti_base , NativeType . I32 , true , module . i32 ( 0 ) ) ;
419+ module . addGlobal ( BuiltinNames . global_closure , NativeType . I32 , true , module . i32 ( - 1 ) ) ;
418420 }
419421
420- module . addGlobal ( BuiltinNames . global_closure , NativeType . I32 , true , module . i32 ( - 1 ) ) ;
421-
422422 // compile entry file(s) while traversing reachable elements
423423 var files = program . filesByName ;
424424 // TODO: for (let file of files.values()) {
@@ -1826,6 +1826,10 @@ export class Compiler extends DiagnosticEmitter {
18261826 var functionTable = this . functionTable ;
18271827 var tableBase = this . options . tableBase ;
18281828 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+ }
18291833 index = tableBase + functionTable . length ;
18301834 if ( ! instance . is ( CommonFlags . TRAMPOLINE ) && instance . signature . requiredParameters < instance . signature . parameterTypes . length ) {
18311835 // insert the trampoline if the function has optional parameters
@@ -3411,7 +3415,7 @@ export class Compiler extends DiagnosticEmitter {
34113415 // pretend to retain the expression immediately so the autorelease, if any, is skipped
34123416 var expr = this . compileExpression ( expression , returnType , constraints | Constraints . WILL_RETAIN ) ;
34133417 var flow = this . currentFlow ;
3414- if ( returnType . isManaged || returnType . signatureReference ) {
3418+ if ( returnType . isManaged ) {
34153419 // check if that worked, and if it didn't, keep the reference alive
34163420 if ( ! this . skippedAutoreleases . has ( expr ) ) {
34173421 let index = this . tryUndoAutorelease ( expr , flow ) ;
@@ -3464,8 +3468,17 @@ export class Compiler extends DiagnosticEmitter {
34643468 }
34653469 fromType = fromType . nonNullableType ;
34663470 }
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
34673473 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 ( ) )
34693482 }
34703483 if ( fromType . isAssignableTo ( toType ) ) { // downcast or same
34713484 assert ( fromType . kind == toType . kind ) ;
@@ -5803,15 +5816,12 @@ export class Compiler extends DiagnosticEmitter {
58035816 // fall-through
58045817 }
58055818 case ElementKind . LOCAL :
5819+ case ElementKind . CLOSEDLOCAL :
58065820 case ElementKind . FIELD : {
58075821 targetType = ( < VariableLikeElement > target ) . type ;
58085822 if ( target . hasDecorator ( DecoratorFlags . UNSAFE ) ) this . checkUnsafe ( expression ) ;
58095823 break ;
58105824 }
5811- case ElementKind . CLOSEDLOCAL : {
5812- targetType = Type . i32
5813- break ;
5814- }
58155825 case ElementKind . PROPERTY_PROTOTYPE : { // static property
58165826 let propertyPrototype = < PropertyPrototype > target ;
58175827 let setterPrototype = propertyPrototype . setterPrototype ;
@@ -5937,21 +5947,12 @@ export class Compiler extends DiagnosticEmitter {
59375947 return this . makeLocalAssignment ( local , valueExpr , valueType , tee ) ;
59385948 }
59395949 case ElementKind . CLOSEDLOCAL : {
5950+ // TODO: ability to update closed over locals
59405951 this . error (
59415952 DiagnosticCode . Not_implemented ,
59425953 valueExpression . range
59435954 ) ;
59445955 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)
59555956 }
59565957 case ElementKind . GLOBAL : {
59575958 let global = < Global > target ;
@@ -6332,18 +6333,19 @@ export class Compiler extends DiagnosticEmitter {
63326333 ) : ExpressionRef {
63336334 var module = this . module ;
63346335 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 ) {
63376339 let local = unchecked ( _values [ i ] ) ;
6338- exprs . push ( module . store (
6340+ let closureClass = type . classReference !
6341+ exprs [ i ] = module . store (
63396342 local . type . byteSize ,
63406343 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+ )
63456348 }
6346- //exprs.push(module.unreachable())
63476349 return module . block ( null , exprs )
63486350 }
63496351
@@ -6424,7 +6426,6 @@ export class Compiler extends DiagnosticEmitter {
64246426
64256427 var signature : Signature | null ;
64266428 var indexArg : ExpressionRef ;
6427- var closedLocal : Local | null = null ;
64286429 switch ( target . kind ) {
64296430 // direct call: concrete function
64306431 case ElementKind . FUNCTION_PROTOTYPE : {
@@ -6451,36 +6452,34 @@ export class Compiler extends DiagnosticEmitter {
64516452 // indirect call: index argument with signature (non-generic, can't be inlined)
64526453 case ElementKind . LOCAL : {
64536454 let local = < Local > target ;
6455+ signature = local . type . signatureReference ;
64546456 if ( local . type . is ( TypeFlags . CLOSURE ) ) {
6455- signature = new Signature ( this . program , [ ] , Type . i32 )
6456- flow . locals = local . type . locals ! ;
6457-
64586457 indexArg = module . block ( null , [
64596458 this . injectClosedLocals (
64606459 this . module . local_get ( local . index , this . options . nativeSizeType ) ,
64616460 local . type
64626461 ) ,
64636462 module . global_set (
64646463 BuiltinNames . global_closure ,
6465- this . module . local_get ( local . index , NativeType . I32 )
6464+ this . module . local_get ( local . index , this . options . nativeSizeType )
64666465 ) ,
64676466 module . load (
6468- 4 ,
6467+ local . type . byteSize ,
64696468 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 ,
64726471 0
64736472 )
6474- ] , NativeType . I32 ) ;
6473+ ] , this . options . nativeSizeType ) ;
64756474 break ;
64766475 }
6477- signature = local . type . signatureReference ;
64786476 if ( signature ) {
64796477 if ( local . is ( CommonFlags . INLINED ) ) {
64806478 indexArg = module . i32 ( i64_low ( local . constantIntegerValue ) ) ;
64816479 } else {
64826480 indexArg = module . local_get ( local . index , NativeType . I32 ) ;
64836481 }
6482+ // If the 16 is divisible by 16, then it must be a closure, so we call it as such
64846483 indexArg = module . if (
64856484 module . binary (
64866485 BinaryOp . EqI32 ,
@@ -6496,14 +6495,14 @@ export class Compiler extends DiagnosticEmitter {
64966495 BuiltinNames . global_closure ,
64976496 indexArg
64986497 ) ,
6499- module . load (
6498+ module . load ( //TODO: support 8 byte addrs
65006499 4 ,
65016500 true ,
65026501 indexArg ,
65036502 NativeType . I32 ,
65046503 0
65056504 )
6506- ] , NativeType . I32 ) ,
6505+ ] , this . options . nativeSizeType ) ,
65076506 indexArg
65086507 )
65096508 break ;
@@ -6607,32 +6606,15 @@ export class Compiler extends DiagnosticEmitter {
66076606 return module . unreachable ( ) ;
66086607 }
66096608 }
6610- var callExpr = this . compileCallIndirect (
6609+
6610+ return this . compileCallIndirect (
66116611 assert ( signature ) , // FIXME: asc can't see this yet
66126612 indexArg ,
66136613 expression . arguments ,
66146614 expression ,
66156615 0 ,
66166616 contextualType == Type . void
66176617 ) ;
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 ;
66366618 }
66376619
66386620 private compileCallExpressionBuiltin (
@@ -7129,7 +7111,6 @@ export class Compiler extends DiagnosticEmitter {
71297111 var module = this . module ;
71307112 var flow = this . currentFlow ;
71317113 var nativeSizeType = this . options . nativeSizeType ;
7132- assert ( false )
71337114 if ( alreadyRetained ) {
71347115 // (t1=newExpr), __release(oldExpr), t1
71357116 // it is important that `newExpr` evaluates before `oldExpr` is released, hence the local
@@ -7196,7 +7177,6 @@ export class Compiler extends DiagnosticEmitter {
71967177 //
71977178 // callReceivingAReference((__release(t = callReturningAReference()), t))
71987179 //
7199- assert ( false )
72007180 var local = flow . getAutoreleaseLocal ( type ) ;
72017181 if ( flow . isNonnull ( expr , type ) ) flow . setLocalFlag ( local . index , LocalFlags . NONNULL ) ;
72027182 return this . module . local_tee ( local . index , expr ) ;
@@ -7723,8 +7703,8 @@ export class Compiler extends DiagnosticEmitter {
77237703 var instance : Function | null ;
77247704 var contextualTypeArguments = makeMap ( flow . contextualTypeArguments ) ;
77257705
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.
77287708 if ( contextualSignature ) {
77297709 let signatureNode = prototype . functionTypeNode ;
77307710 let parameterNodes = signatureNode . parameters ;
@@ -7833,28 +7813,36 @@ export class Compiler extends DiagnosticEmitter {
78337813 }
78347814
78357815 var index = this . ensureFunctionTableEntry ( instance ) ; // reports
7836- console . log ( "function index" + index )
78377816
78387817 if ( index < 0 ) return this . module . unreachable ( ) ;
78397818
78407819 if ( instance . closedLocals . size > 0 ) {
7820+ //Create field declarations for the function and each closed local
78417821 var members = Array < DeclarationStatement > ( instance . closedLocals . size + 1 ) ;
78427822 members [ 0 ] = this . program . makeNativeMember ( "__functionPtr" , "u32" )
78437823 for ( let _values = Map_values ( instance . closedLocals ) , i = 0 , k = _values . length ; i < k ; ++ i ) {
78447824 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 ( ) )
78467826 }
78477827
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 ( ) ,
78507831 members
7851- ) ! ;
7832+ ) ) ;
7833+
7834+ //Resolve this prototype to get the class
78527835 var closureClass = this . resolver . resolveClass ( closureClassPrototype , null ) ! ;
7836+
7837+ //Compile this class to get the type
78537838 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
78577844 var tempLocal = flow . getTempLocal ( this . currentType ) ;
7845+
78587846 //copied closed locals into type
78597847 this . currentType . locals = instance . closedLocals ;
78607848
@@ -8125,13 +8113,19 @@ export class Compiler extends DiagnosticEmitter {
81258113 case ElementKind . CLOSEDLOCAL : {
81268114 let closedLocal = < ClosedLocal > target ;
81278115
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 ,
81308120 true ,
81318121 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
81348124 ) ;
8125+
8126+ flow . actualFunction . nextGlobalClosureOffset += closedLocal . type . byteSize ;
8127+
8128+ return loadExpr
81358129 }
81368130 }
81378131 this . error (
@@ -9452,7 +9446,6 @@ export class Compiler extends DiagnosticEmitter {
94529446 break ;
94539447 }
94549448 default : {
9455- assert ( false ) ;
94569449 return module . unreachable ( ) ;
94579450 }
94589451 }
0 commit comments