/* * Copyright 2014 JBoss Inc * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.dynjs.ir; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.dynjs.ir.instructions.ResultInstruction; import org.dynjs.ir.operands.Label; import org.dynjs.ir.operands.LocalVariable; import org.dynjs.ir.operands.TemporaryLocalVariable; import org.dynjs.ir.operands.TemporaryVariable; import org.dynjs.ir.operands.Variable; import org.dynjs.ir.representations.BasicBlock; import org.dynjs.ir.representations.CFG; import org.dynjs.ir.representations.CFGLinearizer; import org.jruby.dirgra.DirectedGraph; // FIXME: Modelled as single scope now but I doubt this will hold for long. public class Scope { private Scope parent; private Map<Integer, Variable> temporaryVariables = new HashMap<>(); private int temporaryVariablesIndex = 0; private Map<String, LocalVariable> localVariables = new HashMap<>(); // What next variable index will be (also happens to be current size private int localVariablesIndex = 0; private List<Instruction> instructions = new ArrayList<>(); private Map<String, Integer> nextVarIndex = new HashMap<>(); private HashMap<Integer, Integer> rescueMap; private boolean isStrict; private String fileName; public Scope(Scope parent, String fileName, boolean isStrict) { this.parent = parent; this.fileName = fileName; this.isStrict = isStrict; } public Instruction addInstruction(Instruction instruction) { instructions.add(instruction); return instruction; } public String getFileName() { return fileName; } public boolean isStrict() { return isStrict; } public List<Instruction> getInstructions() { return instructions; } /** * Tries to find a variable or returns null if it cannot. This * will walk all scopes to find a captured variable. */ public LocalVariable findVariable(String name) { return findVariable(name, 0); } /** * Tries to find a variable or returns null if it cannot. This * will walk all scopes to find a captured variable. */ public LocalVariable findVariable(String name, int depth) { LocalVariable variable = localVariables.get(name); if (variable != null) { // Destined scope need adjusted variable since it need to know how deep to look for it. if (depth != 0) { return new LocalVariable(name, variable.getOffset(), depth); } return variable; } if (parent != null) { return parent.findVariable(name, depth + 1); } return null; } public int getLocalVariableSize() { return localVariablesIndex; } /** * Return an existing variable or return a new one made in this scope. */ public Variable acquireLocalVariable(String name) { int depth = 0; LocalVariable variable = findVariable(name, depth); if (variable == null) { variable = new LocalVariable(name, localVariablesIndex, 0); localVariables.put(name, variable); localVariablesIndex++; } return variable; } // FIXME: Do I care about all the boxing here of index? public Variable acquireTemporaryVariable(int index) { Variable variable = temporaryVariables.get(index); return variable == null ? createTemporaryVariable() : variable; } public Variable createTemporaryVariable() { Variable variable = new TemporaryVariable(temporaryVariablesIndex); temporaryVariablesIndex++; return variable; } public Variable createTemporaryVariableFor(LocalVariable local) { Variable variable = new TemporaryLocalVariable(local.getName(), temporaryVariablesIndex); temporaryVariablesIndex++; return variable; } public int getTemporaryVariableSize() { return temporaryVariablesIndex; } protected int getPrefixCountSize(String prefix) { Integer index = nextVarIndex.get(prefix); if (index == null) return 0; return index.intValue(); } protected int allocateNextPrefixedName(String prefix) { int index = getPrefixCountSize(prefix); nextVarIndex.put(prefix, index + 1); return index; } public Label getNewLabel(String prefix) { return new Label(prefix, allocateNextPrefixedName(prefix)); } public Label getNewLabel() { return getNewLabel("LBL"); } public Instruction[] prepareForInterpret() { final CFG cfg = new CFG(this); final DirectedGraph<BasicBlock> graph = cfg.build(getInstructions()); // FIXME: Add debug config for this // System.out.println(cfg.toStringInstrs()); // System.out.println(graph.toString()); // FIXME: We don't save this linearized set yet List<BasicBlock> linearizedCFG = CFGLinearizer.linearize(cfg); // FIXME: This is not safe to do in all cases. We need more knowledge to make it 100% safe // FIXME: This is happening twice once for interp and once for compilation. Do only once renameLocalVariables(linearizedCFG); return prepareIPCs(linearizedCFG, cfg); } private void setupLocalVarReplacement(LocalVariable v, Map<Operand, Operand> varRenameMap) { if (varRenameMap.get(v) == null) varRenameMap.put(v, createTemporaryVariableFor(v)); } public List<BasicBlock> prepareForCompilation() { final CFG cfg = new CFG(this); cfg.build(getInstructions()); List<BasicBlock> linearizedCFG = CFGLinearizer.linearize(cfg); renameLocalVariables(linearizedCFG); return linearizedCFG; } private void renameLocalVariables(List<BasicBlock> linearizedCFG) { Map<Operand, Operand> renameMap = new HashMap<Operand, Operand>(); //System.out.println("BEFORE:"); for (BasicBlock b: linearizedCFG) { for (Instruction i: b.getInstructions()) { //System.out.println("I: " + i); if (i instanceof ResultInstruction) { Variable v = ((ResultInstruction) i).getResult(); if (v instanceof LocalVariable) { setupLocalVarReplacement((LocalVariable) v, renameMap); } } for (Variable v : i.getUsedVariables()) { if (v instanceof LocalVariable) { setupLocalVarReplacement((LocalVariable) v, renameMap); } } } } //System.out.println("AFTER:"); // Rename all local var uses with their tmp-var stand-ins for (BasicBlock b: linearizedCFG) { for (Instruction i: b.getInstructions()) { i.renameVariables(renameMap); //System.out.println("I: " + i); } } } private static Label[] catLabels(Label[] labels, Label cat) { if (labels == null) return new Label[] {cat}; Label[] newLabels = new Label[labels.length + 1]; System.arraycopy(labels, 0, newLabels, 0, labels.length); newLabels[labels.length] = cat; return newLabels; } private Instruction[] prepareIPCs(List<BasicBlock> list, CFG cfg) { // Set up IPCs List<Instruction> newInstrs = new ArrayList<Instruction>(); HashMap<Label, Integer> labelIPCMap = new HashMap<Label, Integer>(); int ipc = 0; for (BasicBlock basicBlock: list) { Label label = basicBlock.getLabel(); labelIPCMap.put(label, ipc); // This assumes if multiple equal/same labels exist which are scattered around the scope // must be the same Java instance or only this one will get a targetPC set. label.setTargetIPC(ipc); List<Instruction> bbInstrs = basicBlock.getInstructions(); int bbInstrsLength = bbInstrs.size(); for (int i = 0; i < bbInstrsLength; i++) { Instruction instr = bbInstrs.get(i); newInstrs.add(instr); instr.setIPC(ipc); ipc++; } } cfg.getExitBB().getLabel().setTargetIPC(ipc + 1); // Exit BasicBlock IPC setupRescueMap(list, cfg); // Set up the rescue map return newInstrs.toArray(new Instruction[newInstrs.size()]); } public void setupRescueMap(List<BasicBlock> list, CFG cfg) { rescueMap = new HashMap<Integer, Integer>(); for (BasicBlock basicBlock : list) { BasicBlock rescuerBB = cfg.getRescuerBBFor(basicBlock); int rescuerPC = (rescuerBB == null) ? -1 : rescuerBB.getLabel().getTargetIPC(); for (Instruction instruction : basicBlock.getInstructions()) { rescueMap.put(instruction.getIPC(), rescuerPC); } } } }