< prev index next >

src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java

Print this page




 240     private final DebugLogger log;
 241 
 242     /** From what size should we use spill instead of fields for JavaScript objects? */
 243     static final int OBJECT_SPILL_THRESHOLD = Options.getIntProperty("nashorn.spill.threshold", 256);
 244 
 245     private final Set<String> emittedMethods = new HashSet<>();
 246 
 247     // Function Id -> ContinuationInfo. Used by compilation of rest-of function only.
 248     private ContinuationInfo continuationInfo;
 249 
 250     private final Deque<Label> scopeEntryLabels = new ArrayDeque<>();
 251 
 252     private static final Label METHOD_BOUNDARY = new Label("");
 253     private final Deque<Label> catchLabels = new ArrayDeque<>();
 254     // Number of live locals on entry to (and thus also break from) labeled blocks.
 255     private final IntDeque labeledBlockBreakLiveLocals = new IntDeque();
 256 
 257     //is this a rest of compilation
 258     private final int[] continuationEntryPoints;
 259 



 260     /**
 261      * Constructor.
 262      *
 263      * @param compiler
 264      */
 265     CodeGenerator(final Compiler compiler, final int[] continuationEntryPoints) {
 266         super(new CodeGeneratorLexicalContext());
 267         this.compiler                = compiler;
 268         this.evalCode                = compiler.getSource().isEvalCode();
 269         this.continuationEntryPoints = continuationEntryPoints;
 270         this.callSiteFlags           = compiler.getScriptEnvironment()._callsite_flags;
 271         this.log                     = initLogger(compiler.getContext());
 272     }
 273 
 274     @Override
 275     public DebugLogger getLogger() {
 276         return log;
 277     }
 278 
 279     @Override


1261             method.breakLabel(entryLabel, lc.getUsedSlotCount());
1262         } else {
1263             method.label(entryLabel);
1264         }
1265         if(!method.isReachable()) {
1266             return false;
1267         }
1268         if(lc.isFunctionBody() && emittedMethods.contains(lc.getCurrentFunction().getName())) {
1269             return false;
1270         }
1271         initLocals(block);
1272 
1273         assert lc.getUsedSlotCount() == method.getFirstTemp();
1274         return true;
1275     }
1276 
1277     boolean useOptimisticTypes() {
1278         return !lc.inSplitNode() && compiler.useOptimisticTypes();
1279     }
1280 








1281     @Override
1282     public Node leaveBlock(final Block block) {
1283         popBlockScope(block);
1284         method.beforeJoinPoint(block);
1285 
1286         closeBlockVariables(block);
1287         lc.releaseSlots();
1288         assert !method.isReachable() || (lc.isFunctionBody() ? 0 : lc.getUsedSlotCount()) == method.getFirstTemp() :
1289             "reachable="+method.isReachable() +
1290             " isFunctionBody=" + lc.isFunctionBody() +
1291             " usedSlotCount=" + lc.getUsedSlotCount() +
1292             " firstTemp=" + method.getFirstTemp();
1293 
1294         return block;
1295     }
1296 
1297     private void popBlockScope(final Block block) {
1298         final Label breakLabel = block.getBreakLabel();
1299 



1300         if(!block.needsScope() || lc.isFunctionBody()) {
1301             emitBlockBreakLabel(breakLabel);
1302             return;
1303         }
1304 
1305         final Label beginTryLabel = scopeEntryLabels.pop();
1306         final Label recoveryLabel = new Label("block_popscope_catch");
1307         emitBlockBreakLabel(breakLabel);
1308         final boolean bodyCanThrow = breakLabel.isAfter(beginTryLabel);
1309         if(bodyCanThrow) {
1310             method._try(beginTryLabel, breakLabel, recoveryLabel);
1311         }
1312 
1313         Label afterCatchLabel = null;
1314 
1315         if(method.isReachable()) {
1316             popScope();
1317             if(bodyCanThrow) {
1318                 afterCatchLabel = new Label("block_after_catch");
1319                 method._goto(afterCatchLabel);


1795             }
1796 
1797             @Override
1798             protected void evaluate() {
1799                 new OptimisticOperation((Optimistic)forNode.getInit(), TypeBounds.UNBOUNDED) {
1800                     @Override
1801                     void loadStack() {
1802                         method.load(ITERATOR_TYPE, iterSlot);
1803                     }
1804 
1805                     @Override
1806                     void consumeStack() {
1807                         method.invoke(interfaceCallNoLookup(ITERATOR_CLASS, "next", Object.class));
1808                         convertOptimisticReturnValue();
1809                     }
1810                 }.emit();
1811             }
1812         }.store();
1813         body.accept(this);
1814 








1815         if(method.isReachable()) {
1816             method._goto(continueLabel);
1817         }
1818         method.label(breakLabel);
1819     }
1820 
1821     /**
1822      * Initialize the slots in a frame to undefined.
1823      *
1824      * @param block block with local vars.
1825      */
1826     private void initLocals(final Block block) {
1827         lc.onEnterBlock(block);
1828 
1829         final boolean isFunctionBody = lc.isFunctionBody();
1830         final FunctionNode function = lc.getCurrentFunction();
1831         if (isFunctionBody) {
1832             initializeMethodParameters(function);
1833             if(!function.isVarArg()) {
1834                 expandParameterSlots(function);


1906                         }
1907                     }
1908 
1909                     tuples.add(new MapTuple<Symbol>(symbol.getName(), symbol, paramType, paramSymbol) {
1910                         //this symbol will be put fielded, we can't initialize it as undefined with a known type
1911                         @Override
1912                         public Class<?> getValueType() {
1913                             if (!useDualFields() ||  value == null || paramType == null || paramType.isBoolean()) {
1914                                 return Object.class;
1915                             }
1916                             return paramType.getTypeClass();
1917                         }
1918                     });
1919                 }
1920             }
1921 
1922             /*
1923              * Create a new object based on the symbols and values, generate
1924              * bootstrap code for object
1925              */
1926             new FieldObjectCreator<Symbol>(this, tuples, true, hasArguments) {
1927                 @Override
1928                 protected void loadValue(final Symbol value, final Type type) {
1929                     method.load(value, type);
1930                 }
1931             }.makeObject(method);




1932             // program function: merge scope into global
1933             if (isFunctionBody && function.isProgram()) {
1934                 method.invoke(ScriptRuntime.MERGE_SCOPE);
1935             }
1936 
1937             method.storeCompilerConstant(SCOPE);
1938             if(!isFunctionBody) {
1939                 // Function body doesn't need a try/catch to restore scope, as it'd be a dead store anyway. Allowing it
1940                 // actually causes issues with UnwarrantedOptimismException handlers as ASM will sort this handler to
1941                 // the top of the exception handler table, so it'll be triggered instead of the UOE handlers.
1942                 final Label scopeEntryLabel = new Label("scope_entry");
1943                 scopeEntryLabels.push(scopeEntryLabel);
1944                 method.label(scopeEntryLabel);
1945             }
1946         } else if (isFunctionBody && function.isVarArg()) {
1947             // Since we don't have a scope, parameters didn't get assigned array indices by the FieldObjectCreator, so
1948             // we need to assign them separately here.
1949             int nextParam = 0;
1950             for (final IdentNode param : function.getParameters()) {
1951                 param.getSymbol().setFieldIndex(nextParam++);


4463         private void epilogue() {
4464             /**
4465              * Take the original target args from the stack and use them
4466              * together with the value to be stored to emit the store code
4467              *
4468              * The case that targetSymbol is in scope (!hasSlot) and we actually
4469              * need to do a conversion on non-equivalent types exists, but is
4470              * very rare. See for example test/script/basic/access-specializer.js
4471              */
4472             target.accept(new SimpleNodeVisitor() {
4473                 @Override
4474                 protected boolean enterDefault(final Node node) {
4475                     throw new AssertionError("Unexpected node " + node + " in store epilogue");
4476                 }
4477 
4478                 @Override
4479                 public boolean enterIdentNode(final IdentNode node) {
4480                     final Symbol symbol = node.getSymbol();
4481                     assert symbol != null;
4482                     if (symbol.isScope()) {
4483                         final int flags = getScopeCallSiteFlags(symbol);
4484                         if (isFastScope(symbol)) {
4485                             storeFastScopeVar(symbol, flags);
4486                         } else {
4487                             method.dynamicSet(node.getName(), flags, false);
4488                         }
4489                     } else {
4490                         final Type storeType = assignNode.getType();
4491                         assert storeType != Type.LONG;
4492                         if (symbol.hasSlotFor(storeType)) {
4493                             // Only emit a convert for a store known to be live; converts for dead stores can
4494                             // give us an unnecessary ClassCastException.
4495                             method.convert(storeType);
4496                         }
4497                         storeIdentWithCatchConversion(node, storeType);
4498                     }
4499                     return false;
4500 
4501                 }
4502 
4503                 @Override




 240     private final DebugLogger log;
 241 
 242     /** From what size should we use spill instead of fields for JavaScript objects? */
 243     static final int OBJECT_SPILL_THRESHOLD = Options.getIntProperty("nashorn.spill.threshold", 256);
 244 
 245     private final Set<String> emittedMethods = new HashSet<>();
 246 
 247     // Function Id -> ContinuationInfo. Used by compilation of rest-of function only.
 248     private ContinuationInfo continuationInfo;
 249 
 250     private final Deque<Label> scopeEntryLabels = new ArrayDeque<>();
 251 
 252     private static final Label METHOD_BOUNDARY = new Label("");
 253     private final Deque<Label> catchLabels = new ArrayDeque<>();
 254     // Number of live locals on entry to (and thus also break from) labeled blocks.
 255     private final IntDeque labeledBlockBreakLiveLocals = new IntDeque();
 256 
 257     //is this a rest of compilation
 258     private final int[] continuationEntryPoints;
 259 
 260     // Scope object creators needed for for-of and for-in loops
 261     private Deque<FieldObjectCreator<?>> scopeObjectCreators = new ArrayDeque<>();
 262 
 263     /**
 264      * Constructor.
 265      *
 266      * @param compiler
 267      */
 268     CodeGenerator(final Compiler compiler, final int[] continuationEntryPoints) {
 269         super(new CodeGeneratorLexicalContext());
 270         this.compiler                = compiler;
 271         this.evalCode                = compiler.getSource().isEvalCode();
 272         this.continuationEntryPoints = continuationEntryPoints;
 273         this.callSiteFlags           = compiler.getScriptEnvironment()._callsite_flags;
 274         this.log                     = initLogger(compiler.getContext());
 275     }
 276 
 277     @Override
 278     public DebugLogger getLogger() {
 279         return log;
 280     }
 281 
 282     @Override


1264             method.breakLabel(entryLabel, lc.getUsedSlotCount());
1265         } else {
1266             method.label(entryLabel);
1267         }
1268         if(!method.isReachable()) {
1269             return false;
1270         }
1271         if(lc.isFunctionBody() && emittedMethods.contains(lc.getCurrentFunction().getName())) {
1272             return false;
1273         }
1274         initLocals(block);
1275 
1276         assert lc.getUsedSlotCount() == method.getFirstTemp();
1277         return true;
1278     }
1279 
1280     boolean useOptimisticTypes() {
1281         return !lc.inSplitNode() && compiler.useOptimisticTypes();
1282     }
1283 
1284     // Determine whether a block needs to provide its scope object creator for use by its child nodes.
1285     // This is only necessary for synthetic parent blocks of for-in loops with lexical declarations.
1286     boolean providesScopeCreator(final Block block) {
1287         return block.needsScope() &&  compiler.getScriptEnvironment()._es6
1288                 && block.isSynthetic() && (block.getLastStatement() instanceof ForNode)
1289                 && ((ForNode) block.getLastStatement()).needsScopeCreator();
1290     }
1291 
1292     @Override
1293     public Node leaveBlock(final Block block) {
1294         popBlockScope(block);
1295         method.beforeJoinPoint(block);
1296 
1297         closeBlockVariables(block);
1298         lc.releaseSlots();
1299         assert !method.isReachable() || (lc.isFunctionBody() ? 0 : lc.getUsedSlotCount()) == method.getFirstTemp() :
1300             "reachable="+method.isReachable() +
1301             " isFunctionBody=" + lc.isFunctionBody() +
1302             " usedSlotCount=" + lc.getUsedSlotCount() +
1303             " firstTemp=" + method.getFirstTemp();
1304 
1305         return block;
1306     }
1307 
1308     private void popBlockScope(final Block block) {
1309         final Label breakLabel = block.getBreakLabel();
1310 
1311         if (providesScopeCreator(block)) {
1312             scopeObjectCreators.pop();
1313         }
1314         if(!block.needsScope() || lc.isFunctionBody()) {
1315             emitBlockBreakLabel(breakLabel);
1316             return;
1317         }
1318 
1319         final Label beginTryLabel = scopeEntryLabels.pop();
1320         final Label recoveryLabel = new Label("block_popscope_catch");
1321         emitBlockBreakLabel(breakLabel);
1322         final boolean bodyCanThrow = breakLabel.isAfter(beginTryLabel);
1323         if(bodyCanThrow) {
1324             method._try(beginTryLabel, breakLabel, recoveryLabel);
1325         }
1326 
1327         Label afterCatchLabel = null;
1328 
1329         if(method.isReachable()) {
1330             popScope();
1331             if(bodyCanThrow) {
1332                 afterCatchLabel = new Label("block_after_catch");
1333                 method._goto(afterCatchLabel);


1809             }
1810 
1811             @Override
1812             protected void evaluate() {
1813                 new OptimisticOperation((Optimistic)forNode.getInit(), TypeBounds.UNBOUNDED) {
1814                     @Override
1815                     void loadStack() {
1816                         method.load(ITERATOR_TYPE, iterSlot);
1817                     }
1818 
1819                     @Override
1820                     void consumeStack() {
1821                         method.invoke(interfaceCallNoLookup(ITERATOR_CLASS, "next", Object.class));
1822                         convertOptimisticReturnValue();
1823                     }
1824                 }.emit();
1825             }
1826         }.store();
1827         body.accept(this);
1828 
1829         if (forNode.needsScopeCreator() && providesScopeCreator(lc.getCurrentBlock())) {
1830             // for-in loops with lexical declaration need a new scope for each iteration.
1831             final FieldObjectCreator<?> creator = scopeObjectCreators.peek();
1832             assert creator != null;
1833             creator.createForInIterationScope(method);
1834             method.storeCompilerConstant(SCOPE);
1835         }
1836 
1837         if(method.isReachable()) {
1838             method._goto(continueLabel);
1839         }
1840         method.label(breakLabel);
1841     }
1842 
1843     /**
1844      * Initialize the slots in a frame to undefined.
1845      *
1846      * @param block block with local vars.
1847      */
1848     private void initLocals(final Block block) {
1849         lc.onEnterBlock(block);
1850 
1851         final boolean isFunctionBody = lc.isFunctionBody();
1852         final FunctionNode function = lc.getCurrentFunction();
1853         if (isFunctionBody) {
1854             initializeMethodParameters(function);
1855             if(!function.isVarArg()) {
1856                 expandParameterSlots(function);


1928                         }
1929                     }
1930 
1931                     tuples.add(new MapTuple<Symbol>(symbol.getName(), symbol, paramType, paramSymbol) {
1932                         //this symbol will be put fielded, we can't initialize it as undefined with a known type
1933                         @Override
1934                         public Class<?> getValueType() {
1935                             if (!useDualFields() ||  value == null || paramType == null || paramType.isBoolean()) {
1936                                 return Object.class;
1937                             }
1938                             return paramType.getTypeClass();
1939                         }
1940                     });
1941                 }
1942             }
1943 
1944             /*
1945              * Create a new object based on the symbols and values, generate
1946              * bootstrap code for object
1947              */
1948             final FieldObjectCreator<Symbol> creator = new FieldObjectCreator<Symbol>(this, tuples, true, hasArguments) {
1949                 @Override
1950                 protected void loadValue(final Symbol value, final Type type) {
1951                     method.load(value, type);
1952                 }
1953             };
1954             creator.makeObject(method);
1955             if (providesScopeCreator(block)) {
1956                 scopeObjectCreators.push(creator);
1957             }
1958             // program function: merge scope into global
1959             if (isFunctionBody && function.isProgram()) {
1960                 method.invoke(ScriptRuntime.MERGE_SCOPE);
1961             }
1962 
1963             method.storeCompilerConstant(SCOPE);
1964             if(!isFunctionBody) {
1965                 // Function body doesn't need a try/catch to restore scope, as it'd be a dead store anyway. Allowing it
1966                 // actually causes issues with UnwarrantedOptimismException handlers as ASM will sort this handler to
1967                 // the top of the exception handler table, so it'll be triggered instead of the UOE handlers.
1968                 final Label scopeEntryLabel = new Label("scope_entry");
1969                 scopeEntryLabels.push(scopeEntryLabel);
1970                 method.label(scopeEntryLabel);
1971             }
1972         } else if (isFunctionBody && function.isVarArg()) {
1973             // Since we don't have a scope, parameters didn't get assigned array indices by the FieldObjectCreator, so
1974             // we need to assign them separately here.
1975             int nextParam = 0;
1976             for (final IdentNode param : function.getParameters()) {
1977                 param.getSymbol().setFieldIndex(nextParam++);


4489         private void epilogue() {
4490             /**
4491              * Take the original target args from the stack and use them
4492              * together with the value to be stored to emit the store code
4493              *
4494              * The case that targetSymbol is in scope (!hasSlot) and we actually
4495              * need to do a conversion on non-equivalent types exists, but is
4496              * very rare. See for example test/script/basic/access-specializer.js
4497              */
4498             target.accept(new SimpleNodeVisitor() {
4499                 @Override
4500                 protected boolean enterDefault(final Node node) {
4501                     throw new AssertionError("Unexpected node " + node + " in store epilogue");
4502                 }
4503 
4504                 @Override
4505                 public boolean enterIdentNode(final IdentNode node) {
4506                     final Symbol symbol = node.getSymbol();
4507                     assert symbol != null;
4508                     if (symbol.isScope()) {
4509                         final int flags = getScopeCallSiteFlags(symbol) | (node.isDeclaredHere() ? CALLSITE_DECLARE : 0);
4510                         if (isFastScope(symbol)) {
4511                             storeFastScopeVar(symbol, flags);
4512                         } else {
4513                             method.dynamicSet(node.getName(), flags, false);
4514                         }
4515                     } else {
4516                         final Type storeType = assignNode.getType();
4517                         assert storeType != Type.LONG;
4518                         if (symbol.hasSlotFor(storeType)) {
4519                             // Only emit a convert for a store known to be live; converts for dead stores can
4520                             // give us an unnecessary ClassCastException.
4521                             method.convert(storeType);
4522                         }
4523                         storeIdentWithCatchConversion(node, storeType);
4524                     }
4525                     return false;
4526 
4527                 }
4528 
4529                 @Override


< prev index next >