blob: 165028af359919da08f526d14e35d8a7c6509a74 [file] [log] [blame]
// Copyright (c) 2018, 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 '../compiler/js_metalet.dart' as JS;
import '../compiler/js_names.dart' as JS;
import '../compiler/js_utils.dart' as JS;
import '../js_ast/js_ast.dart' as JS;
import '../js_ast/js_ast.dart' show js;
/// Shared code between Analyzer and Kernel backends.
///
/// This class should only implement functionality that depends purely on JS
/// classes, rather than on Analyzer/Kernel types.
abstract class SharedCompiler {
/// When inside a `[]=` operator, this will be a non-null value that should be
/// returned by any `return;` statement.
///
/// This lets DDC use the setter method's return value directly.
final List<JS.Identifier> _operatorSetResultStack = [];
/// When compiling the body of a `operator []=` method, this will be non-null
/// and will indicate the the value that should be returned from any `return;`
/// statements.
JS.Identifier get _operatorSetResult {
var stack = _operatorSetResultStack;
return stack.isEmpty ? null : stack.last;
}
/// The import URI of current library.
Uri get currentLibraryUri;
void enterFunction(String name, List<JS.Parameter> formals,
bool Function() isLastParamMutated) {
if (name == '[]=') {
_operatorSetResultStack.add(isLastParamMutated()
? new JS.TemporaryId((formals.last as JS.Identifier).name)
: formals.last);
} else {
_operatorSetResultStack.add(null);
}
}
JS.Block exitFunction(
String name, List<JS.Parameter> formals, JS.Block code) {
if (name == "==" &&
formals.isNotEmpty &&
currentLibraryUri.scheme != 'dart') {
// In Dart `operator ==` methods are not called with a null argument.
// This is handled before calling them. For performance reasons, we push
// this check inside the method, to simplify our `equals` helper.
//
// TODO(jmesserly): in most cases this check is not necessary, because
// the Dart code already handles it (typically by an `is` check).
// Eliminate it when possible.
code = js
.block('{ if (# == null) return false; #; }', [formals.first, code]);
}
var setOperatorResult = _operatorSetResultStack.removeLast();
if (setOperatorResult != null) {
// []= methods need to return the value. We could also address this at
// call sites, but it's less code size to handle inside the operator.
var valueParam = formals.last;
var statements = code.statements;
if (statements.isEmpty || !statements.last.alwaysReturns) {
statements.add(new JS.Return(setOperatorResult));
}
if (!identical(setOperatorResult, valueParam)) {
// If the value parameter was mutated, then we use a temporary
// variable to track the initial value
formals.last = setOperatorResult;
code = js
.block('{ let # = #; #; }', [valueParam, setOperatorResult, code]);
}
}
return code;
}
/// Emits a return statement `return <value>;`, handling special rules for
/// the `operator []=` method.
JS.Statement emitReturnStatement(JS.Expression value) {
if (_operatorSetResult != null) {
var result = new JS.Return(_operatorSetResult);
return value != null
? new JS.Block([value.toStatement(), result])
: result;
}
return value != null ? value.toReturn() : new JS.Return();
}
}