@@ -2054,20 +2054,23 @@ export class Compiler extends DiagnosticEmitter {
2054
2054
2055
2055
// === Table ====================================================================================
2056
2056
2057
+ private registerFunctionInTable ( instance : Function ) : u32 {
2058
+ // Add to the function table
2059
+ let functionTable = this . functionTable ;
2060
+ let tableBase = this . options . tableBase ;
2061
+ if ( ! tableBase ) tableBase = 1 ; // leave first elem blank
2062
+ let index = tableBase + functionTable . length ;
2063
+ functionTable . push ( instance ) ;
2064
+ return index ;
2065
+ }
2066
+
2057
2067
/** Ensures that a runtime counterpart of the specified function exists and returns its address. */
2058
2068
ensureRuntimeFunction ( instance : Function ) : i64 {
2059
2069
assert ( instance . is ( CommonFlags . Compiled ) && ! instance . is ( CommonFlags . Stub ) ) ;
2060
2070
let program = this . program ;
2061
2071
let memorySegment = instance . memorySegment ;
2062
2072
if ( ! memorySegment ) {
2063
-
2064
- // Add to the function table
2065
- let functionTable = this . functionTable ;
2066
- let tableBase = this . options . tableBase ;
2067
- if ( ! tableBase ) tableBase = 1 ; // leave first elem blank
2068
- let index = tableBase + functionTable . length ;
2069
- functionTable . push ( instance ) ;
2070
-
2073
+ let index = this . registerFunctionInTable ( instance ) ;
2071
2074
// Create runtime function
2072
2075
let rtInstance = assert ( this . resolver . resolveClass ( program . functionPrototype , [ instance . type ] ) ) ;
2073
2076
let buf = rtInstance . createBuffer ( ) ;
@@ -7026,12 +7029,96 @@ export class Compiler extends DiagnosticEmitter {
7026
7029
return module . unreachable ( ) ;
7027
7030
}
7028
7031
7032
+ compileFirstClassFunction (
7033
+ expression : FunctionExpression ,
7034
+ contextualType : Type ,
7035
+ constraints : Constraints
7036
+ ) : ExpressionRef {
7037
+ let module = this . module ;
7038
+ let instance = this . doCompileFunctionExpression ( expression , contextualType , constraints ) ;
7039
+ let currentType = this . currentType ;
7040
+ if ( ! instance ) return module . unreachable ( ) ;
7041
+ let rtInstance = assert ( this . resolver . resolveClass ( this . program . functionPrototype , [ instance . type ] ) ) ;
7042
+ const functionIndexInTable = this . registerFunctionInTable ( instance ) ;
7043
+ let ctor = this . ensureConstructor ( rtInstance , expression ) ;
7044
+ let indexMember = assert ( rtInstance . getMember ( "_index" ) ) ;
7045
+ assert ( indexMember . kind == ElementKind . PropertyPrototype ) ;
7046
+ let indexProperty = assert ( ( < PropertyPrototype > indexMember ) . instance ) ;
7047
+ let indexSetter = assert ( indexProperty . setterInstance ) ;
7048
+ let tmp = this . currentFlow . getTempLocal ( Type . i32 ) ;
7049
+ let expr = module . block ( null , [
7050
+ this . makeCallDirect (
7051
+ indexSetter ,
7052
+ [
7053
+ module . local_tee (
7054
+ tmp . index ,
7055
+ this . makeCallDirect ( ctor , [ module . i32 ( 0 ) ] , expression , /*immediatelyDropped*/ false ) ,
7056
+ /*isManaged*/ true , // TODO(maybe can be false to optimize performance)
7057
+ TypeRef . I32
7058
+ ) ,
7059
+ module . i32 ( functionIndexInTable ) ,
7060
+ ] ,
7061
+ expression
7062
+ ) ,
7063
+ module . local_get ( tmp . index , TypeRef . I32 )
7064
+ ] , TypeRef . I32 ) ;
7065
+ this . currentType = currentType ;
7066
+ return expr ;
7067
+ }
7068
+
7029
7069
private compileFunctionExpression (
7030
7070
expression : FunctionExpression ,
7031
7071
contextualType : Type ,
7032
7072
constraints : Constraints
7033
7073
) : ExpressionRef {
7034
- let declaration = expression . declaration . clone ( ) ; // generic contexts can have multiple
7074
+ let declaration = expression . declaration ;
7075
+ let isNamed = declaration . name . text . length > 0 ;
7076
+ let module = this . module ;
7077
+ let flow = this . currentFlow ;
7078
+ let isSemanticallyAnonymous = ! isNamed || contextualType != Type . void ;
7079
+
7080
+ let instance = this . doCompileFunctionExpression ( expression , contextualType , constraints ) ;
7081
+ if ( ! instance ) return module . unreachable ( ) ;
7082
+ let offset = this . ensureRuntimeFunction ( instance ) ; // reports
7083
+ let expr = this . options . isWasm64
7084
+ ? module . i64 ( i64_low ( offset ) , i64_high ( offset ) )
7085
+ : module . i32 ( i64_low ( offset ) ) ;
7086
+
7087
+ // add a constant local referring to the function if applicable
7088
+ if ( ! isSemanticallyAnonymous ) {
7089
+ let fname = instance . name ;
7090
+ let existingLocal = flow . getScopedLocal ( fname ) ;
7091
+ if ( existingLocal ) {
7092
+ if ( ! existingLocal . declaration . range . source . isNative ) {
7093
+ this . errorRelated (
7094
+ DiagnosticCode . Duplicate_identifier_0 ,
7095
+ declaration . name . range ,
7096
+ existingLocal . declaration . name . range ,
7097
+ fname
7098
+ ) ;
7099
+ } else { // scoped locals are shared temps that don't track declarations
7100
+ this . error (
7101
+ DiagnosticCode . Duplicate_identifier_0 ,
7102
+ declaration . name . range , fname
7103
+ ) ;
7104
+ }
7105
+ } else {
7106
+ let ftype = instance . type ;
7107
+ let local = flow . addScopedLocal ( instance . name , ftype ) ;
7108
+ flow . setLocalFlag ( local . index , LocalFlags . Constant | LocalFlags . Initialized ) ;
7109
+ expr = module . local_tee ( local . index , expr , ftype . isManaged ) ;
7110
+ }
7111
+ }
7112
+
7113
+ return expr ;
7114
+ }
7115
+
7116
+ private doCompileFunctionExpression (
7117
+ expression : FunctionExpression ,
7118
+ contextualType : Type ,
7119
+ constraints : Constraints
7120
+ ) : Function | null {
7121
+ let declaration = expression . declaration ;
7035
7122
assert ( ! declaration . typeParameters ) ; // function expression cannot be generic
7036
7123
let flow = this . currentFlow ;
7037
7124
let sourceFunction = flow . sourceFunction ;
@@ -7047,7 +7134,6 @@ export class Compiler extends DiagnosticEmitter {
7047
7134
) ;
7048
7135
let instance : Function | null ;
7049
7136
let contextualTypeArguments = cloneMap ( flow . contextualTypeArguments ) ;
7050
- let module = this . module ;
7051
7137
7052
7138
// compile according to context. this differs from a normal function in that omitted parameter
7053
7139
// and return types can be inferred and omitted arguments can be replaced with dummies.
@@ -7065,7 +7151,7 @@ export class Compiler extends DiagnosticEmitter {
7065
7151
DiagnosticCode . Expected_0_arguments_but_got_1 ,
7066
7152
expression . range , numParameters . toString ( ) , numPresentParameters . toString ( )
7067
7153
) ;
7068
- return module . unreachable ( ) ;
7154
+ return null ;
7069
7155
}
7070
7156
7071
7157
// check non-omitted parameter types
@@ -7077,13 +7163,13 @@ export class Compiler extends DiagnosticEmitter {
7077
7163
sourceFunction . parent ,
7078
7164
contextualTypeArguments
7079
7165
) ;
7080
- if ( ! resolvedType ) return module . unreachable ( ) ;
7166
+ if ( ! resolvedType ) return null ;
7081
7167
if ( ! parameterTypes [ i ] . isStrictlyAssignableTo ( resolvedType ) ) {
7082
7168
this . error (
7083
7169
DiagnosticCode . Type_0_is_not_assignable_to_type_1 ,
7084
7170
parameterNode . range , parameterTypes [ i ] . toString ( ) , resolvedType . toString ( )
7085
7171
) ;
7086
- return module . unreachable ( ) ;
7172
+ return null ;
7087
7173
}
7088
7174
}
7089
7175
// any unused parameters are inherited but ignored
@@ -7097,7 +7183,7 @@ export class Compiler extends DiagnosticEmitter {
7097
7183
sourceFunction . parent ,
7098
7184
contextualTypeArguments
7099
7185
) ;
7100
- if ( ! resolvedType ) return module . unreachable ( ) ;
7186
+ if ( ! resolvedType ) return null ;
7101
7187
if (
7102
7188
returnType == Type . void
7103
7189
? resolvedType != Type . void
@@ -7107,7 +7193,7 @@ export class Compiler extends DiagnosticEmitter {
7107
7193
DiagnosticCode . Type_0_is_not_assignable_to_type_1 ,
7108
7194
signatureNode . returnType . range , resolvedType . toString ( ) , returnType . toString ( )
7109
7195
) ;
7110
- return module . unreachable ( ) ;
7196
+ return null ;
7111
7197
}
7112
7198
}
7113
7199
@@ -7120,20 +7206,20 @@ export class Compiler extends DiagnosticEmitter {
7120
7206
DiagnosticCode . _this_cannot_be_referenced_in_current_location ,
7121
7207
thisTypeNode . range
7122
7208
) ;
7123
- return module . unreachable ( ) ;
7209
+ return null ;
7124
7210
}
7125
7211
let resolvedType = this . resolver . resolveType (
7126
7212
thisTypeNode ,
7127
7213
sourceFunction . parent ,
7128
7214
contextualTypeArguments
7129
7215
) ;
7130
- if ( ! resolvedType ) return module . unreachable ( ) ;
7216
+ if ( ! resolvedType ) return null ;
7131
7217
if ( ! thisType . isStrictlyAssignableTo ( resolvedType ) ) {
7132
7218
this . error (
7133
7219
DiagnosticCode . Type_0_is_not_assignable_to_type_1 ,
7134
7220
thisTypeNode . range , thisType . toString ( ) , resolvedType . toString ( )
7135
7221
) ;
7136
- return module . unreachable ( ) ;
7222
+ return null ;
7137
7223
}
7138
7224
}
7139
7225
@@ -7148,52 +7234,21 @@ export class Compiler extends DiagnosticEmitter {
7148
7234
instance . flow . outer = flow ;
7149
7235
let worked = this . compileFunction ( instance ) ;
7150
7236
this . currentType = contextualSignature . type ;
7151
- if ( ! worked ) return module . unreachable ( ) ;
7237
+ if ( ! worked ) return null ;
7152
7238
7153
7239
// otherwise compile like a normal function
7154
7240
} else {
7155
7241
instance = this . resolver . resolveFunction ( prototype , null , contextualTypeArguments ) ;
7156
- if ( ! instance ) return this . module . unreachable ( ) ;
7242
+ if ( ! instance ) return null ;
7157
7243
instance . flow . outer = flow ;
7158
7244
let worked = this . compileFunction ( instance ) ;
7159
7245
this . currentType = instance . signature . type ;
7160
- if ( ! worked ) return module . unreachable ( ) ;
7246
+ if ( ! worked ) return null ;
7161
7247
}
7162
-
7163
- let offset = this . ensureRuntimeFunction ( instance ) ; // reports
7164
- let expr = this . options . isWasm64
7165
- ? module . i64 ( i64_low ( offset ) , i64_high ( offset ) )
7166
- : module . i32 ( i64_low ( offset ) ) ;
7167
-
7168
- // add a constant local referring to the function if applicable
7169
- if ( ! isSemanticallyAnonymous ) {
7170
- let fname = instance . name ;
7171
- let existingLocal = flow . getScopedLocal ( fname ) ;
7172
- if ( existingLocal ) {
7173
- if ( ! existingLocal . declaration . range . source . isNative ) {
7174
- this . errorRelated (
7175
- DiagnosticCode . Duplicate_identifier_0 ,
7176
- declaration . name . range ,
7177
- existingLocal . declaration . name . range ,
7178
- fname
7179
- ) ;
7180
- } else { // scoped locals are shared temps that don't track declarations
7181
- this . error (
7182
- DiagnosticCode . Duplicate_identifier_0 ,
7183
- declaration . name . range , fname
7184
- ) ;
7185
- }
7186
- } else {
7187
- let ftype = instance . type ;
7188
- let local = flow . addScopedLocal ( instance . name , ftype ) ;
7189
- flow . setLocalFlag ( local . index , LocalFlags . Constant | LocalFlags . Initialized ) ;
7190
- expr = module . local_tee ( local . index , expr , ftype . isManaged ) ;
7191
- }
7192
- }
7193
-
7194
- return expr ;
7248
+ return instance ;
7195
7249
}
7196
7250
7251
+
7197
7252
/** Makes sure the enclosing source file of the specified expression has been compiled. */
7198
7253
private maybeCompileEnclosingSource ( expression : Expression ) : void {
7199
7254
let internalPath = expression . range . source . internalPath ;
0 commit comments