blob: eb2a0672ba4b0262b3744545b8519d76867c71e3 [file] [log] [blame] [edit]
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:kernel/ast.dart';
import 'package:wasm_builder/wasm_builder.dart' as w;
import 'translator.dart';
/// Handles lazy initialization of static fields.
class Globals {
final Translator translator;
/// Maps a static field to its global holding the field value.
final Map<Field, w.GlobalBuilder> _globals = {};
/// When a global is read from a module other than the module defining it,
/// this maps the global to the getter function defined and exported in
/// the defining module.
final Map<w.Global, w.BaseFunction> _globalGetters = {};
final Map<Field, w.Global> _globalInitializedFlag = {};
final WasmGlobalImporter _globalsModuleMap;
Globals(this.translator)
: _globalsModuleMap = WasmGlobalImporter(translator, 'global');
Constant? _getConstantInitializer(Field variable) {
Expression? init = variable.initializer;
if (init == null || init is NullLiteral) return NullConstant();
if (init is IntLiteral) return IntConstant(init.value);
if (init is DoubleLiteral) return DoubleConstant(init.value);
if (init is BoolLiteral) return BoolConstant(init.value);
if (init is StringLiteral) return StringConstant(init.value);
if (init is ConstantExpression) return init.constant;
return null;
}
/// Reads the value of [w.Global] onto the stack in [b].
///
/// Takes into account the calling module and the module the global belongs
/// to. If they are not the same then accesses the global indirectly, either
/// through an import or a getter call.
w.ValueType readGlobal(w.InstructionsBuilder b, w.Global global) {
final owningModule = global.enclosingModule;
final callingModule = b.module;
if (owningModule == callingModule) {
b.global_get(global);
} else if (translator.isMainModule(owningModule)) {
final importedGlobal = _globalsModuleMap.get(global, callingModule);
b.global_get(importedGlobal);
} else {
final getter = _globalGetters.putIfAbsent(global, () {
final getterType =
owningModule.types.defineFunction(const [], [global.type.type]);
final getterFunction = owningModule.functions.define(getterType);
final getterBody = getterFunction.body;
getterBody.global_get(global);
getterBody.end();
return getterFunction;
});
translator.callFunction(getter, b);
}
return global.type.type;
}
/// Return (and if needed create) the Wasm global corresponding to a static
/// field.
w.Global getGlobalForStaticField(Field field) {
assert(!field.isLate);
return _globals.putIfAbsent(field, () {
final Constant? init = _getConstantInitializer(field);
w.ValueType fieldType = translator.translateTypeOfField(field);
final module = translator.moduleForReference(field.fieldReference);
final memberName = field.toString();
if (init != null &&
!(translator.constants.ensureConstant(init)?.isLazy ?? false)) {
// Initialized to a constant
final global = module.globals.define(
w.GlobalType(fieldType, mutable: !field.isFinal), memberName);
translator.constants
.instantiateConstant(global.initializer, init, fieldType);
global.initializer.end();
return global;
} else {
final w.ValueType globalType;
if (fieldType is w.RefType && !fieldType.nullable) {
// Null signals uninitialized
globalType = fieldType.withNullability(true);
} else {
// Explicit initialization flag
globalType = fieldType;
final flag = module.globals
.define(w.GlobalType(w.NumType.i32), "$memberName initialized");
flag.initializer.i32_const(0);
flag.initializer.end();
_globalInitializedFlag[field] = flag;
}
final global =
module.globals.define(w.GlobalType(globalType), memberName);
translator
.getDummyValuesCollectorForModule(module)
.instantiateDummyValue(global.initializer, globalType);
global.initializer.end();
// Add initializer function to the compilation queue.
translator.functions.getFunction(field.fieldReference);
return global;
}
});
}
/// Return the Wasm global containing the flag indicating whether this static
/// field has been initialized, if such a flag global is needed.
///
/// Note that [getGlobalForStaticField] must have been called for the field beforehand.
w.Global? getGlobalInitializedFlag(Field variable) =>
_globalInitializedFlag[variable];
}