/* NSC -- new Scala compiler * Copyright 2005-2012 LAMP/EPFL * @author Martin Odersky */ package scala.tools.nsc package backend package jvm import scala.collection.{immutable, mutable} import scala.tools.nsc.symtab._ import scala.tools.asm import GenBCode._ import BackendReporting._ import scala.tools.nsc.backend.jvm.BCodeHelpers.InvokeStyle /* * * @author Miguel Garcia, http://lamp.epfl.ch/~magarcia/ScalaCompilerCornerReloaded/ * @version 1.0 * */ abstract class BCodeSkelBuilder extends BCodeHelpers { import global._ import bTypes._ import coreBTypes._ /* * There's a dedicated PlainClassBuilder for each CompilationUnit, * which simplifies the initialization of per-class data structures in `genPlainClass()` which in turn delegates to `initJClass()` * * The entry-point to emitting bytecode instructions is `genDefDef()` where the per-method data structures are initialized, * including `resetMethodBookkeeping()` and `initJMethod()`. * Once that's been done, and assuming the method being visited isn't abstract, `emitNormalMethodBody()` populates * the ASM MethodNode instance with ASM AbstractInsnNodes. * * Given that CleanUp delivers trees that produce values on the stack, * the entry-point to all-things instruction-emit is `genLoad()`. * There, an operation taking N arguments results in recursively emitting instructions to lead each of them, * followed by emitting instructions to process those arguments (to be found at run-time on the operand-stack). * * In a few cases the above recipe deserves more details, as provided in the documentation for: * - `genLoadTry()` * - `genSynchronized() * - `jumpDest` , `cleanups` , `labelDefsAtOrUnder` */ abstract class PlainSkelBuilder(cunit: CompilationUnit) extends BCClassGen with BCAnnotGen with BCInnerClassGen with JAndroidBuilder with BCForwardersGen with BCPickles with BCJGenSigGen { // Strangely I can't find this in the asm code 255, but reserving 1 for "this" final val MaximumJvmParameters = 254 // current class var cnode: asm.tree.ClassNode = null var thisBType: ClassBType = null var claszSymbol: Symbol = null var isCZParcelable = false var isCZStaticModule = false var isCZRemote = false /* ---------------- idiomatic way to ask questions to typer ---------------- */ def paramTKs(app: Apply): List[BType] = { val Apply(fun, _) = app val funSym = fun.symbol funSym.info.paramTypes map typeToBType } def symInfoTK(sym: Symbol): BType = typeToBType(sym.info) def tpeTK(tree: Tree): BType = typeToBType(tree.tpe) def log(msg: => AnyRef) { global synchronized { global.log(msg) } } /* ---------------- helper utils for generating classes and fields ---------------- */ def genPlainClass(cd: ClassDef) { assert(cnode == null, "GenBCode detected nested methods.") claszSymbol = cd.symbol isCZParcelable = isAndroidParcelableClass(claszSymbol) isCZStaticModule = isStaticModuleClass(claszSymbol) isCZRemote = isRemote(claszSymbol) thisBType = classBTypeFromSymbol(claszSymbol) cnode = new asm.tree.ClassNode() initJClass(cnode) val hasStaticCtor = methodSymbols(cd) exists (_.isStaticConstructor) if (!hasStaticCtor) { // but needs one ... if (isCZStaticModule || isCZParcelable) { fabricateStaticInit() } } val optSerial: Option[Long] = serialVUID(claszSymbol) if (optSerial.isDefined) { addSerialVUID(optSerial.get, cnode)} addClassFields() gen(cd.impl) cnode.visitAttribute(thisBType.inlineInfoAttribute.get) if (AsmUtils.traceClassEnabled && cnode.name.contains(AsmUtils.traceClassPattern)) AsmUtils.traceClass(cnode) assert(cd.symbol == claszSymbol, "Someone messed up BCodePhase.claszSymbol during genPlainClass().") } // end of method genPlainClass() /* * must-single-thread */ private def initJClass(jclass: asm.ClassVisitor) { val bType = classBTypeFromSymbol(claszSymbol) val superClass = bType.info.get.superClass.getOrElse(ObjectRef).internalName val interfaceNames = bType.info.get.interfaces.map(_.internalName) val flags = javaFlags(claszSymbol) val thisSignature = getGenericSignature(claszSymbol, claszSymbol.owner) cnode.visit(classfileVersion, flags, thisBType.internalName, thisSignature, superClass, interfaceNames.toArray) if (emitSource) { cnode.visitSource(cunit.source.toString, null /* SourceDebugExtension */) } enclosingMethodAttribute(claszSymbol, internalName, methodBTypeFromSymbol(_).descriptor) match { case Some(EnclosingMethodEntry(className, methodName, methodDescriptor)) => cnode.visitOuterClass(className, methodName, methodDescriptor) case _ => () } val ssa = getAnnotPickle(thisBType.internalName, claszSymbol) cnode.visitAttribute(if (ssa.isDefined) pickleMarkerLocal else pickleMarkerForeign) emitAnnotations(cnode, claszSymbol.annotations ++ ssa) if (isCZStaticModule || isCZParcelable) { if (isCZStaticModule) { addModuleInstanceField() } } else { if (!settings.noForwarders) { val lmoc = claszSymbol.companionModule // add static forwarders if there are no name conflicts; see bugs #363 and #1735 if (lmoc != NoSymbol) { // it must be a top level class (name contains no $s) val isCandidateForForwarders = { exitingPickler { !(lmoc.name.toString contains '$') && lmoc.hasModuleFlag && !lmoc.isNestedClass } } if (isCandidateForForwarders) { log(s"Adding static forwarders from '$claszSymbol' to implementations in '$lmoc'") addForwarders(isRemote(claszSymbol), cnode, thisBType.internalName, lmoc.moduleClass) } } } } // the invoker is responsible for adding a class-static constructor. } // end of method initJClass /* * can-multi-thread */ private def addModuleInstanceField() { // TODO confirm whether we really don't want ACC_SYNTHETIC nor ACC_DEPRECATED // SD-194 This can't be FINAL on JVM 1.9+ because we assign it from within the // instance constructor, not from directly. Assignment from , // after the constructor has completely finished, seems like the principled // thing to do, but it would change behaviour when "benign" cyclic references // between modules exist. val mods = GenBCode.PublicStatic val fv = cnode.visitField(mods, strMODULE_INSTANCE_FIELD, thisBType.descriptor, null, // no java-generic-signature null // no initial value ) fv.visitEnd() } /* * must-single-thread */ private def fabricateStaticInit() { val clinit: asm.MethodVisitor = cnode.visitMethod( GenBCode.PublicStatic, // TODO confirm whether we really don't want ACC_SYNTHETIC nor ACC_DEPRECATED CLASS_CONSTRUCTOR_NAME, "()V", null, // no java-generic-signature null // no throwable exceptions ) clinit.visitCode() /* "legacy static initialization" */ if (isCZStaticModule) { clinit.visitTypeInsn(asm.Opcodes.NEW, thisBType.internalName) clinit.visitMethodInsn(asm.Opcodes.INVOKESPECIAL, thisBType.internalName, INSTANCE_CONSTRUCTOR_NAME, "()V", false) } if (isCZParcelable) { legacyAddCreatorCode(clinit, cnode, thisBType.internalName) } clinit.visitInsn(asm.Opcodes.RETURN) clinit.visitMaxs(0, 0) // just to follow protocol, dummy arguments clinit.visitEnd() } def addClassFields() { for (f <- fieldSymbols(claszSymbol)) { val javagensig = getGenericSignature(f, claszSymbol) val flags = javaFieldFlags(f) val jfield = new asm.tree.FieldNode( flags, f.javaSimpleName.toString, symInfoTK(f).descriptor, javagensig, null // no initial value ) cnode.fields.add(jfield) emitAnnotations(jfield, f.annotations) } } // end of method addClassFields() // current method var mnode: asm.tree.MethodNode = null var jMethodName: String = null var isMethSymStaticCtor = false var returnType: BType = null var methSymbol: Symbol = null // in GenASM this is local to genCode(), ie should get false whenever a new method is emitted (including fabricated ones eg addStaticInit()) var isModuleInitialized = false // used by genLoadTry() and genSynchronized() var earlyReturnVar: Symbol = null var shouldEmitCleanup = false // line numbers var lastEmittedLineNr = -1 object bc extends JCodeMethodN { override def jmethod = PlainSkelBuilder.this.mnode } /* ---------------- Part 1 of program points, ie Labels in the ASM world ---------------- */ /* * A jump is represented as an Apply node whose symbol denotes a LabelDef, the target of the jump. * The `jumpDest` map is used to: * (a) find the asm.Label for the target, given an Apply node's symbol; * (b) anchor an asm.Label in the instruction stream, given a LabelDef node. * In other words, (a) is necessary when visiting a jump-source, and (b) when visiting a jump-target. * A related map is `labelDef`: it has the same keys as `jumpDest` but its values are LabelDef nodes not asm.Labels. * */ var jumpDest: immutable.Map[ /* LabelDef */ Symbol, asm.Label ] = null def programPoint(labelSym: Symbol): asm.Label = { assert(labelSym.isLabel, s"trying to map a non-label symbol to an asm.Label, at: ${labelSym.pos}") jumpDest.getOrElse(labelSym, { val pp = new asm.Label jumpDest += (labelSym -> pp) pp }) } /* * A program point may be lexically nested (at some depth) * (a) in the try-clause of a try-with-finally expression * (b) in a synchronized block. * Each of the constructs above establishes a "cleanup block" to execute upon * both normal-exit, early-return, and abrupt-termination of the instructions it encloses. * * The `cleanups` LIFO queue represents the nesting of active (for the current program point) * pending cleanups. For each such cleanup an asm.Label indicates the start of its cleanup-block. * At any given time during traversal of the method body, * the head of `cleanups` denotes the cleanup-block for the closest enclosing try-with-finally or synchronized-expression. * * `cleanups` is used: * * (1) upon visiting a Return statement. * In case of pending cleanups, we can't just emit a RETURN instruction, but must instead: * - store the result (if any) in `earlyReturnVar`, and * - jump to the next pending cleanup. * See `genReturn()` * * (2) upon emitting a try-with-finally or a synchronized-expr, * In these cases, the targets of the above jumps are emitted, * provided an early exit was actually encountered somewhere in the protected clauses. * See `genLoadTry()` and `genSynchronized()` * * The code thus emitted for jumps and targets covers the early-return case. * The case of abrupt (ie exceptional) termination is covered by exception handlers * emitted for that purpose as described in `genLoadTry()` and `genSynchronized()`. */ var cleanups: List[asm.Label] = Nil def registerCleanup(finCleanup: asm.Label) { if (finCleanup != null) { cleanups = finCleanup :: cleanups } } def unregisterCleanup(finCleanup: asm.Label) { if (finCleanup != null) { assert(cleanups.head eq finCleanup, s"Bad nesting of cleanup operations: $cleanups trying to unregister: $finCleanup") cleanups = cleanups.tail } } /* ---------------- local variables and params ---------------- */ case class Local(tk: BType, name: String, idx: Int, isSynth: Boolean) /* * Bookkeeping for method-local vars and method-params. * * TODO: use fewer slots. local variable slots are never re-used in separate blocks. * In the following example, x and y could use the same slot. * def foo() = { * { val x = 1 } * { val y = "a" } * } */ object locals { private val slots = mutable.Map.empty[Symbol, Local] // (local-or-param-sym -> Local(BType, name, idx, isSynth)) private var nxtIdx = -1 // next available index for local-var def reset(isStaticMethod: Boolean) { slots.clear() nxtIdx = if (isStaticMethod) 0 else 1 } def contains(locSym: Symbol): Boolean = { slots.contains(locSym) } def apply(locSym: Symbol): Local = { slots.apply(locSym) } /* Make a fresh local variable, ensuring a unique name. * The invoker must make sure inner classes are tracked for the sym's tpe. */ def makeLocal(tk: BType, name: String): Symbol = { val locSym = methSymbol.newVariable(cunit.freshTermName(name), NoPosition, Flags.SYNTHETIC) // setInfo tpe makeLocal(locSym, tk) locSym } def makeLocal(locSym: Symbol): Local = { makeLocal(locSym, symInfoTK(locSym)) } def getOrMakeLocal(locSym: Symbol): Local = { // `getOrElse` below has the same effect as `getOrElseUpdate` because `makeLocal()` adds an entry to the `locals` map. slots.getOrElse(locSym, makeLocal(locSym)) } private def makeLocal(sym: Symbol, tk: BType): Local = { assert(!slots.contains(sym), "attempt to create duplicate local var.") assert(nxtIdx != -1, "not a valid start index") val loc = Local(tk, sym.javaSimpleName.toString, nxtIdx, sym.isSynthetic) slots += (sym -> loc) assert(tk.size > 0, "makeLocal called for a symbol whose type is Unit.") nxtIdx += tk.size loc } // not to be confused with `fieldStore` and `fieldLoad` which also take a symbol but a field-symbol. def store(locSym: Symbol) { val Local(tk, _, idx, _) = slots(locSym) bc.store(idx, tk) } def load(locSym: Symbol) { val Local(tk, _, idx, _) = slots(locSym) bc.load(idx, tk) } } /* ---------------- Part 2 of program points, ie Labels in the ASM world ---------------- */ /* * The semantics of try-with-finally and synchronized-expr require their cleanup code * to be present in three forms in the emitted bytecode: * (a) as normal-exit code, reached via fall-through from the last program point being protected, * (b) as code reached upon early-return from an enclosed return statement. * The only difference between (a) and (b) is their next program-point: * the former must continue with fall-through while * the latter must continue to the next early-return cleanup (if any, otherwise return from the method). * Otherwise they are identical. * (c) as exception-handler, reached via exceptional control flow, * which rethrows the caught exception once it's done with the cleanup code. * * A particular cleanup may in general contain LabelDefs. Care is needed when duplicating such jump-targets, * so as to preserve agreement with the (also duplicated) jump-sources. * This is achieved based on the bookkeeping provided by two maps: * - `labelDefsAtOrUnder` lists all LabelDefs enclosed by a given Tree node (the key) * - `labelDef` provides the LabelDef node whose symbol is used as key. * As a sidenote, a related map is `jumpDest`: it has the same keys as `labelDef` but its values are asm.Labels not LabelDef nodes. * * Details in `emitFinalizer()`, which is invoked from `genLoadTry()` and `genSynchronized()`. */ var labelDefsAtOrUnder: scala.collection.Map[Tree, List[LabelDef]] = null var labelDef: scala.collection.Map[Symbol, LabelDef] = null// (LabelDef-sym -> LabelDef) // bookkeeping the scopes of non-synthetic local vars, to emit debug info (`emitVars`). var varsInScope: List[Tuple2[Symbol, asm.Label]] = null // (local-var-sym -> start-of-scope) // helpers around program-points. def lastInsn: asm.tree.AbstractInsnNode = mnode.instructions.getLast def currProgramPoint(): asm.Label = { lastInsn match { case labnode: asm.tree.LabelNode => labnode.getLabel case _ => val pp = new asm.Label mnode visitLabel pp pp } } def markProgramPoint(lbl: asm.Label) { val skip = (lbl == null) || isAtProgramPoint(lbl) if (!skip) { mnode visitLabel lbl } } def isAtProgramPoint(lbl: asm.Label): Boolean = { (lastInsn match { case labnode: asm.tree.LabelNode => (labnode.getLabel == lbl); case _ => false } ) } def lineNumber(tree: Tree) { if (!emitLines || !tree.pos.isDefined) return; val nr = tree.pos.finalPosition.line if (nr != lastEmittedLineNr) { lastEmittedLineNr = nr lastInsn match { case lnn: asm.tree.LineNumberNode => // overwrite previous landmark as no instructions have been emitted for it lnn.line = nr case _ => mnode.visitLineNumber(nr, currProgramPoint()) } } } // on entering a method def resetMethodBookkeeping(dd: DefDef) { locals.reset(isStaticMethod = methSymbol.isStaticMember) jumpDest = immutable.Map.empty[ /* LabelDef */ Symbol, asm.Label ] // populate labelDefsAtOrUnder val ldf = new LabelDefsFinder ldf.traverse(dd.rhs) labelDefsAtOrUnder = ldf.result.withDefaultValue(Nil) labelDef = labelDefsAtOrUnder(dd.rhs).map(ld => (ld.symbol -> ld)).toMap // check previous invocation of genDefDef exited as many varsInScope as it entered. assert(varsInScope == null, "Unbalanced entering/exiting of GenBCode's genBlock().") // check previous invocation of genDefDef unregistered as many cleanups as it registered. assert(cleanups == Nil, "Previous invocation of genDefDef didn't unregister as many cleanups as it registered.") isModuleInitialized = false earlyReturnVar = null shouldEmitCleanup = false lastEmittedLineNr = -1 } /* ---------------- top-down traversal invoking ASM Tree API along the way ---------------- */ def gen(tree: Tree) { tree match { case EmptyTree => () case _: ModuleDef => abort(s"Modules should have been eliminated by refchecks: $tree") case ValDef(mods, name, tpt, rhs) => () // fields are added in `genPlainClass()`, via `addClassFields()` case dd : DefDef => val sym = dd.symbol if (needsStaticImplMethod(sym)) { if (sym.isMixinConstructor) { val statified = global.gen.mkStatic(dd, sym.name, _.cloneSymbol) genDefDef(statified) } else { val forwarderDefDef = { val dd1 = global.gen.mkStatic(deriveDefDef(dd)(_ => EmptyTree), traitSuperAccessorName(sym), _.cloneSymbol.withoutAnnotations) dd1.symbol.setFlag(Flags.ARTIFACT).resetFlag(Flags.OVERRIDE) val selfParam :: realParams = dd1.vparamss.head.map(_.symbol) deriveDefDef(dd1)(_ => atPos(dd1.pos)( Apply(Select(global.gen.mkAttributedIdent(selfParam).setType(sym.owner.typeConstructor), dd.symbol), realParams.map(global.gen.mkAttributedIdent)).updateAttachment(UseInvokeSpecial)) ) } genDefDef(forwarderDefDef) genDefDef(dd) } } else genDefDef(dd) case Template(_, _, body) => body foreach gen case _ => abort(s"Illegal tree in gen: $tree") } } /* * must-single-thread */ def initJMethod(flags: Int, params: List[Symbol]) { val jgensig = getGenericSignature(methSymbol, claszSymbol) addRemoteExceptionAnnot(isCZRemote, hasPublicBitSet(flags), methSymbol) val (excs, others) = methSymbol.annotations partition (_.symbol == definitions.ThrowsClass) val thrownExceptions: List[String] = getExceptions(excs) val bytecodeName = if (isMethSymStaticCtor) CLASS_CONSTRUCTOR_NAME else jMethodName val mdesc = methodBTypeFromSymbol(methSymbol).descriptor mnode = cnode.visitMethod( flags, bytecodeName, mdesc, jgensig, mkArray(thrownExceptions) ).asInstanceOf[asm.tree.MethodNode] emitParamNames(mnode, params) emitAnnotations(mnode, others) emitParamAnnotations(mnode, params.map(_.annotations)) } // end of method initJMethod def genDefDef(dd: DefDef) { // the only method whose implementation is not emitted: getClass() if (definitions.isGetClass(dd.symbol)) { return } assert(mnode == null, "GenBCode detected nested method.") methSymbol = dd.symbol jMethodName = methSymbol.javaSimpleName.toString returnType = methodBTypeFromSymbol(dd.symbol).returnType isMethSymStaticCtor = methSymbol.isStaticConstructor resetMethodBookkeeping(dd) // add method-local vars for params val DefDef(_, _, _, vparamss, _, rhs) = dd assert(vparamss.isEmpty || vparamss.tail.isEmpty, s"Malformed parameter list: $vparamss") val params = if (vparamss.isEmpty) Nil else vparamss.head for (p <- params) { locals.makeLocal(p.symbol) } // debug assert((params.map(p => locals(p.symbol).tk)) == asmMethodType(methSymbol).getArgumentTypes.toList, "debug") if (params.size > MaximumJvmParameters) { // SI-7324 reporter.error(methSymbol.pos, s"Platform restriction: a parameter list's length cannot exceed $MaximumJvmParameters.") return } val isNative = methSymbol.hasAnnotation(definitions.NativeAttr) val isAbstractMethod = rhs == EmptyTree val flags = GenBCode.mkFlags( javaFlags(methSymbol), if (isAbstractMethod) asm.Opcodes.ACC_ABSTRACT else 0, if (methSymbol.isStrictFP) asm.Opcodes.ACC_STRICT else 0, if (isNative) asm.Opcodes.ACC_NATIVE else 0 // native methods of objects are generated in mirror classes ) initJMethod(flags, params.map(_.symbol)) /* Add method-local vars for LabelDef-params. * * This makes sure that: * (1) upon visiting any "forward-jumping" Apply (ie visited before its target LabelDef), and after * (2) grabbing the corresponding param symbols, * those param-symbols can be used to access method-local vars. * * When duplicating a finally-contained LabelDef, another program-point is needed for the copy (each such copy has its own asm.Label), * but the same vars (given by the LabelDef's params) can be reused, * because no LabelDef ends up nested within itself after such duplication. */ for(ld <- labelDefsAtOrUnder(dd.rhs); ldp <- ld.params; if !locals.contains(ldp.symbol)) { // the tail-calls xform results in symbols shared btw method-params and labelDef-params, thus the guard above. locals.makeLocal(ldp.symbol) } if (!isAbstractMethod && !isNative) { def emitNormalMethodBody() { val veryFirstProgramPoint = currProgramPoint() genLoad(rhs, returnType) rhs match { case Return(_) | Block(_, Return(_)) | Throw(_) | Block(_, Throw(_)) => () case EmptyTree => globalError("Concrete method has no definition: " + dd + ( if (settings.debug) "(found: " + methSymbol.owner.info.decls.toList.mkString(", ") + ")" else "")) case _ => bc emitRETURN returnType } if (emitVars) { // add entries to LocalVariableTable JVM attribute val onePastLastProgramPoint = currProgramPoint() val hasStaticBitSet = ((flags & asm.Opcodes.ACC_STATIC) != 0) if (!hasStaticBitSet) { mnode.visitLocalVariable( "this", thisBType.descriptor, null, veryFirstProgramPoint, onePastLastProgramPoint, 0 ) } for (p <- params) { emitLocalVarScope(p.symbol, veryFirstProgramPoint, onePastLastProgramPoint, force = true) } } if (isMethSymStaticCtor) { appendToStaticCtor(dd) } } // end of emitNormalMethodBody() lineNumber(rhs) emitNormalMethodBody() // Note we don't invoke visitMax, thus there are no FrameNode among mnode.instructions. // The only non-instruction nodes to be found are LabelNode and LineNumberNode. } if (AsmUtils.traceMethodEnabled && mnode.name.contains(AsmUtils.traceMethodPattern)) AsmUtils.traceMethod(mnode) mnode = null } // end of method genDefDef() /* * must-single-thread * * TODO document, explain interplay with `fabricateStaticInit()` */ private def appendToStaticCtor(dd: DefDef) { def insertBefore( location: asm.tree.AbstractInsnNode, i0: asm.tree.AbstractInsnNode, i1: asm.tree.AbstractInsnNode) { if (i0 != null) { mnode.instructions.insertBefore(location, i0.clone(null)) mnode.instructions.insertBefore(location, i1.clone(null)) } } // collect all return instructions var rets: List[asm.tree.AbstractInsnNode] = Nil mnode foreachInsn { i => if (i.getOpcode() == asm.Opcodes.RETURN) { rets ::= i } } if (rets.isEmpty) { return } var insnModA: asm.tree.AbstractInsnNode = null var insnModB: asm.tree.AbstractInsnNode = null // call object's private ctor from static ctor if (isCZStaticModule) { // NEW `moduleName` val className = internalName(methSymbol.enclClass) insnModA = new asm.tree.TypeInsnNode(asm.Opcodes.NEW, className) // INVOKESPECIAL val callee = methSymbol.enclClass.primaryConstructor val jname = callee.javaSimpleName.toString val jowner = internalName(callee.owner) val jtype = methodBTypeFromSymbol(callee).descriptor insnModB = new asm.tree.MethodInsnNode(asm.Opcodes.INVOKESPECIAL, jowner, jname, jtype, false) } var insnParcA: asm.tree.AbstractInsnNode = null var insnParcB: asm.tree.AbstractInsnNode = null // android creator code if (isCZParcelable) { // add a static field ("CREATOR") to this class to cache android.os.Parcelable$Creator val andrFieldDescr = classBTypeFromSymbol(AndroidCreatorClass).descriptor cnode.visitField( asm.Opcodes.ACC_STATIC | asm.Opcodes.ACC_FINAL, "CREATOR", andrFieldDescr, null, null ) // INVOKESTATIC CREATOR(): android.os.Parcelable$Creator; -- TODO where does this Android method come from? val callee = definitions.getMember(claszSymbol.companionModule, androidFieldName) val jowner = internalName(callee.owner) val jname = callee.javaSimpleName.toString val jtype = methodBTypeFromSymbol(callee).descriptor insnParcA = new asm.tree.MethodInsnNode(asm.Opcodes.INVOKESTATIC, jowner, jname, jtype, false) // PUTSTATIC `thisBType.internalName`.CREATOR; insnParcB = new asm.tree.FieldInsnNode(asm.Opcodes.PUTSTATIC, thisBType.internalName, "CREATOR", andrFieldDescr) } // insert a few instructions for initialization before each return instruction for(r <- rets) { insertBefore(r, insnModA, insnModB) insertBefore(r, insnParcA, insnParcB) } } def emitLocalVarScope(sym: Symbol, start: asm.Label, end: asm.Label, force: Boolean = false) { val Local(tk, name, idx, isSynth) = locals(sym) if (force || !isSynth) { mnode.visitLocalVariable(name, tk.descriptor, null, start, end, idx) } } def genLoad(tree: Tree, expectedType: BType) } // end of class PlainSkelBuilder }