| // Copyright (c) 2016, 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 '../closure.dart' show ClosureDataLookup; | |
| import '../constants/constant_system.dart'; | |
| import '../common/codegen.dart' show CodegenRegistry; | |
| import '../common_elements.dart'; | |
| import '../compiler.dart'; | |
| import '../deferred_load.dart'; | |
| import '../diagnostics/diagnostic_listener.dart'; | |
| import '../elements/entities.dart' show Entity, Local, MemberEntity; | |
| import '../elements/jumps.dart'; | |
| import '../elements/types.dart'; | |
| import '../io/source_information.dart'; | |
| import '../js_backend/backend.dart'; | |
| import '../js_backend/backend_usage.dart'; | |
| import '../js_backend/constant_handler_javascript.dart'; | |
| import '../js_backend/interceptor_data.dart'; | |
| import '../js_backend/inferred_data.dart'; | |
| import '../js_backend/js_interop_analysis.dart'; | |
| import '../js_backend/namer.dart'; | |
| import '../js_backend/native_data.dart'; | |
| import '../js_backend/runtime_types.dart'; | |
| import '../js_emitter/code_emitter_task.dart'; | |
| import '../options.dart'; | |
| import '../types/abstract_value_domain.dart'; | |
| import '../types/types.dart'; | |
| import '../world.dart' show JClosedWorld; | |
| import 'jump_handler.dart'; | |
| import 'locals_handler.dart'; | |
| import 'nodes.dart'; | |
| import 'type_builder.dart'; | |
| /// Base class for objects that build up an SSA graph. | |
| /// | |
| /// This contains helpers for building the graph and tracking information about | |
| /// the current state of the graph being built. | |
| abstract class GraphBuilder { | |
| /// Holds the resulting SSA graph. | |
| final HGraph graph = new HGraph(); | |
| // TODO(het): remove this | |
| /// A reference to the compiler. | |
| Compiler get compiler; | |
| /// True if the builder is processing nodes inside a try statement. This is | |
| /// important for generating control flow out of a try block like returns or | |
| /// breaks. | |
| bool inTryStatement = false; | |
| /// The JavaScript backend we are targeting in this compilation. | |
| JavaScriptBackend get backend; | |
| CodegenRegistry get registry; | |
| JClosedWorld get closedWorld; | |
| AbstractValueDomain get abstractValueDomain => | |
| closedWorld.abstractValueDomain; | |
| DiagnosticReporter get reporter => backend.reporter; | |
| CompilerOptions get options => compiler.options; | |
| JCommonElements get commonElements => closedWorld.commonElements; | |
| CodeEmitterTask get emitter => backend.emitter; | |
| GlobalTypeInferenceResults get globalInferenceResults; | |
| ClosureDataLookup get closureDataLookup => | |
| compiler.backendStrategy.closureDataLookup; | |
| NativeData get nativeData => closedWorld.nativeData; | |
| InterceptorData get interceptorData => closedWorld.interceptorData; | |
| BackendUsage get backendUsage => closedWorld.backendUsage; | |
| Namer get namer => backend.namer; | |
| RuntimeTypesNeed get rtiNeed => closedWorld.rtiNeed; | |
| JavaScriptConstantCompiler get constants => backend.constants; | |
| ConstantSystem get constantSystem => constants.constantSystem; | |
| RuntimeTypesEncoder get rtiEncoder => backend.rtiEncoder; | |
| JsInteropAnalysis get jsInteropAnalysis => backend.jsInteropAnalysis; | |
| InferredData get inferredData => globalInferenceResults.inferredData; | |
| DeferredLoadTask get deferredLoadTask => compiler.deferredLoadTask; | |
| DartTypes get types => closedWorld.dartTypes; | |
| /// Used to track the locals while building the graph. | |
| LocalsHandler localsHandler; | |
| /// A stack of instructions. | |
| /// | |
| /// We build the SSA graph by simulating a stack machine. | |
| List<HInstruction> stack = <HInstruction>[]; | |
| /// The count of nested loops we are currently building. | |
| /// | |
| /// The loop nesting is consulted when inlining a function invocation. The | |
| /// inlining heuristics take this information into account. | |
| int loopDepth = 0; | |
| /// A mapping from jump targets to their handlers. | |
| Map<JumpTarget, JumpHandler> jumpTargets = <JumpTarget, JumpHandler>{}; | |
| void push(HInstruction instruction) { | |
| add(instruction); | |
| stack.add(instruction); | |
| } | |
| HInstruction pop() { | |
| return stack.removeLast(); | |
| } | |
| /// Pops the most recent instruction from the stack and 'boolifies' it. | |
| /// | |
| /// Boolification is checking if the value is '=== true'. | |
| HInstruction popBoolified(); | |
| /// Pushes a boolean checking [expression] against null. | |
| pushCheckNull(HInstruction expression) { | |
| push(new HIdentity(expression, graph.addConstantNull(closedWorld), null, | |
| abstractValueDomain.boolType)); | |
| } | |
| void dup() { | |
| stack.add(stack.last); | |
| } | |
| HBasicBlock _current; | |
| /// The current block to add instructions to. Might be null, if we are | |
| /// visiting dead code, but see [isReachable]. | |
| HBasicBlock get current => _current; | |
| void set current(c) { | |
| isReachable = c != null; | |
| _current = c; | |
| } | |
| /// The most recently opened block. Has the same value as [current] while | |
| /// the block is open, but unlike [current], it isn't cleared when the | |
| /// current block is closed. | |
| HBasicBlock lastOpenedBlock; | |
| /// Indicates whether the current block is dead (because it has a throw or a | |
| /// return further up). If this is false, then [current] may be null. If the | |
| /// block is dead then it may also be aborted, but for simplicity we only | |
| /// abort on statement boundaries, not in the middle of expressions. See | |
| /// [isAborted]. | |
| bool isReachable = true; | |
| HParameterValue lastAddedParameter; | |
| Map<Local, HInstruction> parameters = <Local, HInstruction>{}; | |
| HBasicBlock addNewBlock() { | |
| HBasicBlock block = graph.addNewBlock(); | |
| // If adding a new block during building of an expression, it is due to | |
| // conditional expressions or short-circuit logical operators. | |
| return block; | |
| } | |
| void open(HBasicBlock block) { | |
| block.open(); | |
| current = block; | |
| lastOpenedBlock = block; | |
| } | |
| HBasicBlock close(HControlFlow end) { | |
| HBasicBlock result = current; | |
| current.close(end); | |
| current = null; | |
| return result; | |
| } | |
| HBasicBlock closeAndGotoExit(HControlFlow end) { | |
| HBasicBlock result = current; | |
| current.close(end); | |
| current = null; | |
| result.addSuccessor(graph.exit); | |
| return result; | |
| } | |
| void goto(HBasicBlock from, HBasicBlock to) { | |
| from.close(new HGoto(abstractValueDomain)); | |
| from.addSuccessor(to); | |
| } | |
| bool isAborted() { | |
| return current == null; | |
| } | |
| /// Creates a new block, transitions to it from any current block, and | |
| /// opens the new block. | |
| HBasicBlock openNewBlock() { | |
| HBasicBlock newBlock = addNewBlock(); | |
| if (!isAborted()) goto(current, newBlock); | |
| open(newBlock); | |
| return newBlock; | |
| } | |
| void add(HInstruction instruction) { | |
| current.add(instruction); | |
| } | |
| HParameterValue addParameter(Entity parameter, AbstractValue type) { | |
| HParameterValue result = new HParameterValue(parameter, type); | |
| if (lastAddedParameter == null) { | |
| graph.entry.addBefore(graph.entry.first, result); | |
| } else { | |
| graph.entry.addAfter(lastAddedParameter, result); | |
| } | |
| lastAddedParameter = result; | |
| return result; | |
| } | |
| HSubGraphBlockInformation wrapStatementGraph(SubGraph statements) { | |
| if (statements == null) return null; | |
| return new HSubGraphBlockInformation(statements); | |
| } | |
| HSubExpressionBlockInformation wrapExpressionGraph(SubExpression expression) { | |
| if (expression == null) return null; | |
| return new HSubExpressionBlockInformation(expression); | |
| } | |
| /// Returns the current source element. | |
| /// | |
| /// The returned element is a declaration element. | |
| MemberEntity get sourceElement; | |
| HLiteralList buildLiteralList(List<HInstruction> inputs) { | |
| return new HLiteralList(inputs, abstractValueDomain.growableListType); | |
| } | |
| HInstruction callSetRuntimeTypeInfoWithTypeArguments( | |
| InterfaceType type, | |
| List<HInstruction> rtiInputs, | |
| HInstruction newObject, | |
| SourceInformation sourceInformation) { | |
| if (!rtiNeed.classNeedsTypeArguments(type.element)) { | |
| return newObject; | |
| } | |
| HInstruction typeInfo = new HTypeInfoExpression( | |
| TypeInfoExpressionKind.INSTANCE, | |
| closedWorld.elementEnvironment.getThisType(type.element), | |
| rtiInputs, | |
| abstractValueDomain.dynamicType); | |
| add(typeInfo); | |
| return callSetRuntimeTypeInfo(typeInfo, newObject, sourceInformation); | |
| } | |
| /// Called when control flow is about to change, in which case we need to | |
| /// specify special successors if we are already in a try/catch/finally block. | |
| void handleInTryStatement() { | |
| if (!inTryStatement) return; | |
| HBasicBlock block = close(new HExitTry(abstractValueDomain)); | |
| HBasicBlock newBlock = graph.addNewBlock(); | |
| block.addSuccessor(newBlock); | |
| open(newBlock); | |
| } | |
| HInstruction callSetRuntimeTypeInfo(HInstruction typeInfo, | |
| HInstruction newObject, SourceInformation sourceInformation); | |
| /// The element for which this SSA builder is being used. | |
| MemberEntity get targetElement; | |
| TypeBuilder get typeBuilder; | |
| /// Helper to implement JS_GET_FLAG. | |
| /// | |
| /// The concrete SSA graph builder will extract a flag parameter from the | |
| /// JS_GET_FLAG call and then push a boolean result onto the stack. This | |
| /// function provides the boolean value corresponding to the given [flagName]. | |
| /// If [flagName] is not recognized, this function returns `null` and the | |
| /// concrete SSA builder reports an error. | |
| bool getFlagValue(String flagName) { | |
| switch (flagName) { | |
| case 'IS_FULL_EMITTER': | |
| return !options.useStartupEmitter; | |
| case 'MINIFIED': | |
| return options.enableMinification; | |
| case 'MUST_RETAIN_METADATA': | |
| return false; | |
| case 'USE_CONTENT_SECURITY_POLICY': | |
| return options.useContentSecurityPolicy; | |
| default: | |
| return null; | |
| } | |
| } | |
| } |