< 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


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


1283 
1284     @Override
1285     public Node leaveBlock(final Block block) {
1286         popBlockScope(block);
1287         method.beforeJoinPoint(block);
1288 
1289         closeBlockVariables(block);
1290         lc.releaseSlots();
1291         assert !method.isReachable() || (lc.isFunctionBody() ? 0 : lc.getUsedSlotCount()) == method.getFirstTemp() :
1292             "reachable="+method.isReachable() +
1293             " isFunctionBody=" + lc.isFunctionBody() +
1294             " usedSlotCount=" + lc.getUsedSlotCount() +
1295             " firstTemp=" + method.getFirstTemp();
1296 
1297         return block;
1298     }
1299 
1300     private void popBlockScope(final Block block) {
1301         final Label breakLabel = block.getBreakLabel();
1302 
1303         if (block.providesScopeCreator()) {
1304             scopeObjectCreators.pop();
1305         }
1306         if(!block.needsScope() || lc.isFunctionBody()) {
1307             emitBlockBreakLabel(breakLabel);
1308             return;
1309         }
1310 
1311         final Label beginTryLabel = scopeEntryLabels.pop();
1312         final Label recoveryLabel = new Label("block_popscope_catch");
1313         emitBlockBreakLabel(breakLabel);
1314         final boolean bodyCanThrow = breakLabel.isAfter(beginTryLabel);
1315         if(bodyCanThrow) {
1316             method._try(beginTryLabel, breakLabel, recoveryLabel);
1317         }
1318 
1319         Label afterCatchLabel = null;
1320 
1321         if(method.isReachable()) {
1322             popScope();
1323             if(bodyCanThrow) {
1324                 afterCatchLabel = new Label("block_after_catch");
1325                 method._goto(afterCatchLabel);


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


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


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


< prev index next >