Guest User

Untitled

a guest
Feb 18th, 2018
78
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. start = new Date()
  2.  
  3. log =
  4.     verbose: (args...) -> #console.log(args...)
  5.     notify: (args...) -> console.log(args...)
  6.     error: (args...) -> console.error(args...)
  7.  
  8. # modules
  9. jast = require '../jast/jast'
  10.  
  11. #######################################################################
  12. # ringo vs mug
  13. #######################################################################
  14.  
  15. if this.addToClasspath
  16.     # rhino
  17.     this.addToClasspath this.module.resolve("../asm-3.3.jar")
  18.     arguments = this.system.args.slice(1)
  19.     {Opcodes, ClassWriter, FieldVisitor, MethodVisitor, AnnotationVisitor, Label} = this.Packages.org.objectweb.asm
  20.  
  21. else
  22.     # Mug
  23.     java = require('java')
  24.     {Opcodes, ClassWriter, FieldVisitor, MethodVisitor, AnnotationVisitor, Label} = java.import('org.objectweb.asm')
  25.  
  26. #######################################################################
  27. # names and stuff
  28. #######################################################################
  29.  
  30. jvm = {}
  31. jvm.qn =
  32.     object: "java/lang/Object"
  33.     boolean: "java/lang/Boolean"
  34.     number: "java/lang/Number"
  35.     double: "java/lang/Double"
  36.     string: "java/lang/String"
  37.     exception: "java/lang/Exception"
  38.     pattern: "java/util/regex/Pattern"
  39. jvm.sig =
  40.     void: "V"
  41.     double: "D"
  42.     integer: "I"
  43.     boolean: "Z"
  44.     obj: (x) -> "L" + x + ";"
  45.     call: (args..., ret) -> "(" + args.join('') + ")" + (ret or "V")
  46.     array: (x) -> "[" + x
  47.  
  48. mug = {}
  49. mug.qn = {}
  50. mug.qn.pkg = "mug/runtime/"
  51. mug.qn.null = mug.qn.pkg + "JSNull"
  52. mug.qn.boolean = mug.qn.pkg + "JSBoolean"
  53. mug.qn.string = mug.qn.pkg + "JSString"
  54. mug.qn.number = mug.qn.pkg + "JSNumber"
  55. mug.qn.utils = mug.qn.pkg + "JSUtils"
  56. mug.qn.function = mug.qn.pkg + "JSFunction"
  57. mug.qn.object = mug.qn.pkg + "JSObject"
  58. mug.qn.array = mug.qn.pkg + "JSArray"
  59. mug.qn.regexp = mug.qn.pkg + "JSRegExp"
  60. mug.qn.module = mug.qn.pkg + "JSModule"
  61. mug.qn.exception = mug.qn.pkg + "JSException"
  62. mug.qn.valueException = mug.qn.pkg + "JSValueException"
  63. mug.qn.timers = mug.qn.pkg + "JSConcurrency"
  64. mug.qn.toplevel = mug.qn.pkg + "JSEnvironment"
  65.  
  66. compiler = {}
  67.  
  68. compiler.globals = [
  69.   "exports", "require", "print", "console", "arguments"
  70.     "parseInt", "parseFloat", "isNaN", "isFinite"
  71.     "Math", "JSON"
  72.     "Object", "Array", "Number", "String", "Boolean", "Function", "Date"
  73.     "Error", "SyntaxError", "RegExp"
  74.     "setTimeout", "setInterval", "clearTimeout", "clearInterval"
  75.    ]
  76.  
  77. compiler.closureArgCount = 8
  78.  
  79. compiler.qn = {}
  80. compiler.qn.script = -> "js/script"
  81. compiler.qn.pkg = -> compiler.qn.script + "$"
  82. compiler.qn.constants = -> compiler.qn.pkg() + "constants"
  83. compiler.qn.scriptscope = -> compiler.qn.pkg() + "scope$script"
  84. compiler.qn.scope = (i) ->
  85.     if i == 0
  86.         compiler.qn.scriptscope()
  87.     else
  88.         compiler.qn.pkg() + "scope$" + i
  89. compiler.qn.context = (i) ->
  90.     if i == 0
  91.         compiler.qn.script
  92.     else
  93.         compiler.qn.pkg() + "closure$" + i
  94.  
  95. compiler.ident =
  96.     num: (n) -> "NUM_" + n
  97.     str: (n) -> "STR_" + n
  98.     regex: (n) -> "REGEX_" + n
  99.     scope: (n) -> "SCOPE_" + n
  100.     env: 'ENV'
  101.  
  102. compiler.sig = {}
  103. compiler.sig.instantiate = jvm.sig.call(
  104.     jvm.sig.integer # Argument count.
  105.     (jvm.sig.obj(jvm.qn.object) for i in [0...compiler.closureArgCount])... # Args
  106.     jvm.sig.array(jvm.sig.obj(jvm.qn.object)) # Arg overflow array
  107.     jvm.sig.obj(jvm.qn.object) # Return object.
  108.     )
  109. compiler.sig.invoke = (c = compiler.closureArgCount) ->
  110.     jvm.sig.call(
  111.         jvm.sig.obj(jvm.qn.object) # "this" object.
  112.         jvm.sig.integer # Argument Count
  113.         (jvm.sig.obj(jvm.qn.object) for i in [0...c])... # Args
  114.         jvm.sig.array(jvm.sig.obj(jvm.qn.object)) # Arg overflow array
  115.         jvm.sig.obj(jvm.qn.object) # Return object.
  116.     )
  117.  
  118. compiler.regs = {}
  119. compiler.regs.thisObj = 1
  120. compiler.regs.argc = 2
  121. compiler.regs.offset = 3
  122. compiler.regs.scope = compiler.regs.offset + compiler.closureArgCount + 1
  123. compiler.regs.ref = compiler.regs.scope + 1
  124.  
  125. #######################################################################
  126. # utility
  127. #######################################################################
  128.  
  129. compiler.stringArray = (strs...) ->
  130.     arr = java.lang.reflect.Array.newInstance(java.lang.String, strs.length)
  131.     (arr[i] = strs[i]) for i in [0...strs.length]
  132.     return arr
  133.  
  134. #######################################################################
  135. # constants
  136. #######################################################################
  137.  
  138. class ConstantsCompiler
  139.  
  140.     compileClass: ->
  141.         nums = input.numbers
  142.         regexps = input.regexps
  143.  
  144.         # Initialize class.
  145.         cw = new ClassWriter(ClassWriter.COMPUTE_MAXS)
  146.         cw.visit Opcodes.V1_6,
  147.             Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER,
  148.             compiler.qn.constants(),
  149.             null,
  150.             jvm.qn.object,
  151.             null
  152.  
  153.         # Write field definitions.
  154.         for num, i in nums
  155.             cw.visitField(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, compiler.ident.num(i), jvm.sig.obj(jvm.qn.double), null, null).visitEnd()
  156.         for regexp, i in regexps
  157.             cw.visitField(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, compiler.ident.regex(i), jvm.sig.obj(jvm.qn.pattern), null, null).visitEnd()
  158.  
  159.         # Static initializer.
  160.         mv = cw.visitMethod Opcodes.ACC_STATIC, "<clinit>",
  161.             jvm.sig.call(jvm.sig.void), null, null
  162.         mv.visitCode()
  163.  
  164.         # Write numbers.
  165.         for num, i in nums
  166.             mv.visitTypeInsn Opcodes.NEW, jvm.qn.double
  167.             mv.visitInsn Opcodes.DUP
  168.             # Take advantage of AST representation of ints to see if we should decode
  169.             # long numbers like 0xFF00FF00 into (negative) ints or raw doubles
  170.            
  171.             # translate to negative int
  172.             num = new java.lang.Long(num).intValue() if num > 0x7fffffff
  173.             mv.visitLdcInsn new java.lang.Double(num)
  174.             mv.visitMethodInsn Opcodes.INVOKESPECIAL, jvm.qn.double, "<init>",
  175.                 jvm.sig.call(jvm.sig.double, jvm.sig.void)
  176.             mv.visitFieldInsn Opcodes.PUTSTATIC, compiler.qn.constants(),
  177.                 compiler.ident.num(i), jvm.sig.obj(jvm.qn.double)
  178.        
  179.         # Write regexps.
  180.         for [expr, flags], i in regexps
  181.             mv.visitLdcInsn expr
  182.             mv.visitLdcInsn flags
  183.             mv.visitMethodInsn Opcodes.INVOKESTATIC, mug.qn.utils, "compilePattern", jvm.sig.call(jvm.sig.obj(jvm.qn.string), jvm.sig.obj(jvm.qn.string), jvm.sig.obj(jvm.qn.pattern))
  184.             mv.visitFieldInsn Opcodes.PUTSTATIC, compiler.qn.constants(), compiler.ident.regex(i), jvm.sig.obj(jvm.qn.pattern)
  185.            
  186.         mv.visitInsn Opcodes.RETURN
  187.         mv.visitMaxs 0, 0
  188.         mv.visitEnd
  189.  
  190.         # End writing class.
  191.         cw.visitEnd()
  192.         log.verbose '...Compiled constants struct ' + compiler.qn.constants()
  193.         return [compiler.qn.constants() + '.class', cw.toByteArray()]
  194.  
  195. #######################################################################
  196. # code compilation
  197. #######################################################################
  198.  
  199. class MethodCompiler
  200.     constructor: (@ctx) ->
  201.         @lineNumber = -1
  202.         @labels = {}
  203.  
  204.         @vars = ctx.localVars
  205.         @enclosedVars = ctx.enclosedVars
  206.         @unenclosedVars = ctx.unenclosedVars
  207.  
  208.         log.verbose 'Context unenclosed vars:', @unenclosedVars
  209.  
  210.     getNodeCompileType: (n) ->
  211.         return switch n.type
  212.             # Literals.
  213.             when "null-literal" then "null"
  214.             when "boolean-literal" then "boolean"
  215.             when "num-literal" then "number"
  216.             when "undef-literal" then "Object"
  217.             when "str-literal" then "String"
  218.             when "regexp-literal" then "JSRegExp"
  219.             when "array-literal" then "JSArray"
  220.             when "obj-literal" then "JSObject"
  221.             when "func-literal" then "JSFunction"
  222.  
  223.             # Operations.
  224.             when "add-op-expr" #TODO we should know this by output types
  225.                 "Object"
  226.             when "sub-op-expr", "div-op-expr", "mul-op-expr", "mod-op-expr", "lsh-op-expr", "neg-op-expr", "bit-and-op-expr", "bit-or-op-expr", "bit-xor-op-expr", "bit-not-op-expr"
  227.                 "number"
  228.             when "eq-op-expr", "neq-op-expr", "not-op-expr", "eqs-op-expr", "neqs-op-expr", "lt-op-expr", "lte-op-expr", "gt-op-expr", "gte-op-expr", "instanceof-op-expr", "in-op-expr"
  229.                 "boolean"
  230.             when "or-op-expr", "and-op-expr", "if-expr"
  231.                 "Object"
  232.             when "typeof-op-expr" then "String"
  233.             when "void-op-expr" then "Object"
  234.             when "seq-op-expr" then @getNodeCompileType(n.right)
  235.  
  236.             # Expressions.
  237.             when "this-expr", "scope-ref-expr", "static-ref-expr", "dyn-ref-expr", "static-method-call-expr", "call-expr", "new-expr"
  238.                 "Object"
  239.             when "scope-assign-expr", "static-assign-expr", "dyn-assign-expr", "if-expr"
  240.                 "Object" # @getNodeCompileType(n.expr)
  241.            
  242.             when "scope-inc-expr", "static-inc-expr", "dyn-inc-expr"
  243.                 "number"
  244.            
  245.             when "scope-delete-expr", "static-delete-expr", "dyn-delete-expr"
  246.                 "boolean"
  247.  
  248.             else throw new Error "Couldn't match compile type " + n.type
  249.  
  250.     # Assembly utilities.
  251.  
  252.     pushLabel: (label, cont, brk) ->
  253.         if label then @pushLabel("", cont, brk)
  254.         else label = ""
  255.  
  256.         @labels[label] = @labels[label] or []
  257.         @labels[label].push {cont: cont, brk: brk}
  258.         return
  259.  
  260.     popLabel: (label) ->
  261.         if label then @popLabel("")
  262.         else label = ""
  263.        
  264.         return @labels[label]?.pop()
  265.  
  266.     getLabel: (label) ->
  267.         label = label or ""
  268.         return @labels[label]?[@labels[label].length-1]
  269.  
  270.     compilePop: (mv, node) ->
  271.         if @getNodeCompileType(node) == 'number' then mv.visitInsn Opcodes.POP2
  272.         else mv.visitInsn Opcodes.POP
  273.         return
  274.  
  275.     compileLoadInt: (mv, i) ->
  276.         if i >= -128 and i <= 127
  277.             mv.visitIntInsn Opcodes.BIPUSH, i
  278.         else
  279.             mv.visitLdcInsn new java.lang.Integer(i)
  280.         return
  281.  
  282.     # Get reference to the environment scope.
  283.  
  284.     compileLoadEnvironment: (mv) ->
  285.         mv.visitVarInsn Opcodes.ALOAD, 0
  286.         mv.visitFieldInsn Opcodes.GETFIELD, compiler.qn.context(@ctx.id), compiler.ident.env, jvm.sig.obj(mug.qn.toplevel)
  287.         return
  288.  
  289.     # Search scopes for reference. Returns qn of scope object.
  290.  
  291.     compileScopeRef: (mv, name, ln) ->
  292.         log.verbose "Seeking var #{name} in current scope (#{@vars})"
  293.  
  294.         if name in @vars or (@ctx.id == 0 and name in compiler.globals)
  295.             # Variable defined in current scope.
  296.             log.verbose ' . Found in current scope.'
  297.             mv.visitVarInsn Opcodes.ALOAD, compiler.regs.scope
  298.             return compiler.qn.scope(@ctx.id)
  299.        
  300.         # Search parents
  301.         for parent in @ctx.getParents().reverse()
  302.             if name in parent.localVars or (parent.id == 0 and name in compiler.globals)
  303.                 # Variable found in ancestor scope.
  304.                 log.verbose " . Found in parent scope \##{parent.id}."
  305.                 mv.visitVarInsn Opcodes.ALOAD, 0
  306.                 mv.visitFieldInsn Opcodes.GETFIELD, compiler.qn.context(@ctx.id), "SCOPE_" + parent.id, jvm.sig.obj(compiler.qn.scope(parent.id))
  307.                 return compiler.qn.scope(parent.id)
  308.            
  309.         # Not found in parents
  310.         #if name in compiler.globals
  311.         #TODO global object or something
  312.        
  313.         # Not found at all.
  314.         throw new Error "Identifier not found in any scope: #{name} (line #{ln})"
  315.  
  316.     # ASM boxing/unboxing.
  317.  
  318.     compileAsObject: (mv, expr, dupPrimitive = false) ->
  319.         # Number literal optimization
  320.         if expr.type == 'num-literal'
  321.             nums = input.numbers
  322.             if nums.indexOf(expr.value) != -1 #TODO remove all code-gen'd numbers
  323.                 log.verbose ' . Num literal optimization.'
  324.                 mv.visitFieldInsn Opcodes.GETSTATIC, compiler.qn.constants(), compiler.ident.num(nums.indexOf expr.value), jvm.sig.obj(jvm.qn.double)
  325.                 return
  326.  
  327.         switch @getNodeCompileType(expr)
  328.             when "boolean"
  329.                 mv.visitTypeInsn Opcodes.NEW, jvm.qn.boolean
  330.                 mv.visitInsn Opcodes.DUP
  331.             when "number"
  332.                 mv.visitTypeInsn Opcodes.NEW, jvm.qn.double
  333.                 mv.visitInsn Opcodes.DUP
  334.        
  335.         @compileNode mv, expr
  336.  
  337.         switch @getNodeCompileType(expr)
  338.             when 'boolean'
  339.                 if dupPrimitive then mv.visitInsn Opcodes.DUP_X2
  340.                 mv.visitMethodInsn Opcodes.INVOKESPECIAL, jvm.qn.boolean, "<init>", jvm.sig.call(jvm.sig.boolean, jvm.sig.void)
  341.             when 'number'
  342.                 if dupPrimitive then mv.visitInsn Opcodes.DUP_X2
  343.                 mv.visitMethodInsn Opcodes.INVOKESPECIAL, jvm.qn.double, "<init>", jvm.sig.call(jvm.sig.double, jvm.sig.void)
  344.             else
  345.                 if dupPrimitive then mv.visitInsn Opcodes.DUP_X2
  346.        
  347.         return
  348.  
  349.     # as-(primitive) conversion
  350.  
  351.     compileJSObject: (mv, expr) ->
  352.         switch @getNodeCompileType(expr)
  353.             when 'boolean'
  354.                 mv.visitTypeInsn Opcodes.NEW, mug.qn.boolean
  355.                 mv.visitInsn Opcodes.DUP
  356.  
  357.                 @compileLoadEnvironment mv
  358.                 @compileNode mv, expr
  359.  
  360.                 mv.visitMethodInsn Opcodes.INVOKESPECIAL, mug.qn.boolean, "<init>", jvm.sig.call(jvm.sig.obj(mug.qn.toplevel), jvm.sig.boolean, jvm.sig.void)
  361.             when 'number'
  362.                 mv.visitTypeInsn Opcodes.NEW, mug.qn.number
  363.                 mv.visitInsn Opcodes.DUP
  364.  
  365.                 @compileLoadEnvironment mv
  366.                 @compileNode mv, expr
  367.  
  368.                 mv.visitMethodInsn Opcodes.INVOKESPECIAL, mug.qn.number, "<init>", jvm.sig.call(jvm.sig.obj(mug.qn.toplevel), jvm.sig.double, jvm.sig.void)
  369.             when 'String'
  370.                 mv.visitTypeInsn Opcodes.NEW, mug.qn.string
  371.                 mv.visitInsn Opcodes.DUP
  372.  
  373.                 @compileLoadEnvironment mv
  374.                 @compileNode mv, expr
  375.  
  376.                 mv.visitMethodInsn Opcodes.INVOKESPECIAL, mug.qn.string, "<init>", jvm.sig.call(jvm.sig.obj(mug.qn.toplevel), jvm.sig.obj(jvm.qn.string), jvm.sig.void)
  377.             else
  378.                 @compileLoadEnvironment mv
  379.                 @compileNode mv, expr
  380.  
  381.                 mv.visitMethodInsn Opcodes.INVOKESTATIC, mug.qn.utils, "asJSObject", jvm.sig.call(jvm.sig.obj(mug.qn.toplevel), jvm.sig.obj(jvm.qn.object), jvm.sig.obj(mug.qn.object))
  382.         return
  383.  
  384.     compileAsNumber: (mv, type) ->
  385.         switch type
  386.             #TODO when "boolean"
  387.             when "number"
  388.             else
  389.                 mv.visitMethodInsn Opcodes.INVOKESTATIC, mug.qn.utils, "asNumber", jvm.sig.call(jvm.sig.obj(jvm.qn.object), jvm.sig.double)
  390.         return
  391.  
  392.     compileAsBoolean: (mv, type) ->
  393.         switch type
  394.             #TODO when "number"
  395.             #TODO when "String"
  396.             when "boolean"
  397.             else
  398.                 mv.visitMethodInsn Opcodes.INVOKESTATIC, mug.qn.utils, "asBoolean", jvm.sig.call(jvm.sig.obj(jvm.qn.object), jvm.sig.boolean)
  399.         return
  400.  
  401.     # Populate stack with arguments for invoke.
  402.  
  403.     compileInvokeArgs: (mv, args) ->
  404.         # Argument count.
  405.         mv.visitLdcInsn new java.lang.Integer(args.length)
  406.        
  407.         # Automatic arguments.
  408.         for i in [0...compiler.closureArgCount]
  409.             if args[i]
  410.                 @compileAsObject mv, args[i]
  411.             else
  412.                 mv.visitInsn Opcodes.ACONST_NULL
  413.                 #mv.visitInsn Opcodes.DUP
  414.         # Overflow arguments.
  415.         if args.length > compiler.closureArgCount
  416.             @compileLoadInt mv, (args.length - compiler.closureArgCount)
  417.             mv.visitTypeInsn Opcodes.ANEWARRAY, jvm.qn.object
  418.             for i in [compiler.closureArgCount...args.length]
  419.                 mv.visitInsn Opcodes.DUP
  420.                 @compileLoadInt mv, i - compiler.closureArgCount
  421.                 @compileAsObject mv, args[i]
  422.                 mv.visitInsn Opcodes.AASTORE
  423.         else
  424.             mv.visitInsn Opcodes.ACONST_NULL
  425.         return
  426.  
  427.     # Comparison operations.
  428.  
  429.     compileComparison: (mv, op, left, right) ->
  430.         falseCase = new Label(); trueCase = new Label()
  431.  
  432.         @compileNode mv, left
  433.         @compileAsNumber mv, @getNodeCompileType(left)
  434.         @compileNode mv, right
  435.         @compileAsNumber mv, @getNodeCompileType(right)
  436.  
  437.         mv.visitInsn Opcodes.DCMPG
  438.         mv.visitJumpInsn op, trueCase
  439.         mv.visitInsn Opcodes.ICONST_0
  440.         mv.visitJumpInsn Opcodes.GOTO, falseCase
  441.         mv.visitLabel trueCase
  442.         mv.visitInsn Opcodes.ICONST_1
  443.         mv.visitLabel falseCase
  444.         return
  445.    
  446.     # Assign variables to registers when possible.
  447.     # Leave arguments in registers when possible.
  448.  
  449.     getRegister: (name) ->
  450.         return -1 if name not in @unenclosedVars
  451.  
  452.         # closure arguments
  453.         if @ctx.id != 0
  454.             if name in @ctx.node.args
  455.                 return compiler.regs.offset + @ctx.node.args.indexOf(name)
  456.             if name == @ctx.node.name
  457.                 return 0
  458.         # builtin globals
  459.         #TODO these can be scoped too, actually do this work
  460.         if @ctx.id == 0 and name in compiler.globals
  461.             return -1
  462.         # scope variable
  463.         return compiler.regs.ref + @unenclosedVars.indexOf(name)
  464.  
  465.     # The big compile matrix.
  466.  
  467.     compileNode: (mv, n) ->
  468.         # Compile line number.
  469.         if @lineNumber != n.ln
  470.             label = new Label()
  471.             mv.visitLabel label
  472.             mv.visitLineNumber n.ln, label
  473.             @lineNumber = n.ln
  474.  
  475.         # compile something baby
  476.         #console.log 'Compiling node ' + JSON.stringify(n.type)
  477.  
  478.         switch n.type
  479.            
  480.             # Literals.
  481.  
  482.             when 'null-literal'
  483.                 @compileLoadEnvironment mv
  484.                 mv.visitMethodInsn Opcodes.INVOKEVIRTUAL, mug.qn.toplevel, "getNullObject", jvm.sig.call(jvm.sig.obj(mug.qn.null))
  485.            
  486.             when 'boolean-literal'
  487.                 if n.value then mv.visitInsn Opcodes.ICONST_1
  488.                 else mv.visitInsn Opcodes.ICONST_0
  489.            
  490.             when 'undef-literal'
  491.                 mv.visitInsn Opcodes.ACONST_NULL
  492.            
  493.             when 'num-literal'
  494.                 # Take advantage of AST representation of ints to see if we should decode
  495.                 # long numbers like 0xFF00FF00 into (negative) ints or raw doubles
  496.                 # translate to negative int
  497.                 num = n.value
  498.                 num = new java.lang.Long(num).intValue() if num > 0x7fffffff
  499.                 mv.visitLdcInsn new java.lang.Double(num)
  500.                 # TODO that
  501.            
  502.             when 'str-literal'
  503.                 mv.visitLdcInsn n.value
  504.            
  505.             when 'regexp-literal'
  506.                 # Get regexp index.
  507.                 for regex, i in input.regexps
  508.                     break if regex[0] == n.expr and regex[1] == n.flags
  509.  
  510.                 mv.visitTypeInsn Opcodes.NEW, mug.qn.regexp
  511.                 mv.visitInsn Opcodes.DUP
  512.                 @compileLoadEnvironment mv
  513.                 mv.visitFieldInsn Opcodes.GETSTATIC, compiler.qn.constants(), compiler.ident.regex(i), jvm.sig.obj(jvm.qn.pattern)
  514.                 mv.visitLdcInsn n.flags
  515.                 mv.visitMethodInsn Opcodes.INVOKESTATIC, mug.qn.utils, "isPatternGlobal", jvm.sig.call(jvm.sig.obj(jvm.qn.string), jvm.sig.boolean)
  516.                 mv.visitMethodInsn Opcodes.INVOKESPECIAL, mug.qn.regexp, "<init>", jvm.sig.call(jvm.sig.obj(mug.qn.toplevel), jvm.sig.obj(jvm.qn.pattern), jvm.sig.boolean, jvm.sig.void)
  517.            
  518.             when 'array-literal'
  519.                 mv.visitTypeInsn Opcodes.NEW, mug.qn.array
  520.                 mv.visitInsn Opcodes.DUP
  521.                 @compileLoadEnvironment mv
  522.                 @compileLoadInt mv, n.exprs.length
  523.                 mv.visitMethodInsn Opcodes.INVOKESPECIAL, mug.qn.array, "<init>", jvm.sig.call(jvm.sig.obj(mug.qn.toplevel), jvm.sig.integer, jvm.sig.void)
  524.                 mv.visitInsn Opcodes.DUP
  525.                 @compileLoadInt mv, n.exprs.length
  526.                 mv.visitTypeInsn Opcodes.ANEWARRAY, jvm.qn.object
  527.                 for i in [0...n.exprs.length]
  528.                     mv.visitInsn Opcodes.DUP
  529.                     @compileLoadInt mv, i
  530.                     @compileAsObject mv, n.exprs[i]
  531.                     mv.visitInsn Opcodes.AASTORE
  532.                 mv.visitMethodInsn Opcodes.INVOKEVIRTUAL, mug.qn.array, "load", jvm.sig.call(jvm.sig.array(jvm.sig.obj(jvm.qn.object)), jvm.sig.void)
  533.            
  534.             when 'obj-literal'
  535.                 mv.visitTypeInsn Opcodes.NEW, mug.qn.object
  536.                 mv.visitInsn Opcodes.DUP
  537.                 # Object prototype.
  538.                 @compileLoadEnvironment mv
  539.                 mv.visitMethodInsn Opcodes.INVOKESPECIAL, mug.qn.object, "<init>", jvm.sig.call(jvm.sig.obj(mug.qn.toplevel), jvm.sig.void)
  540.                 for {value, expr} in n.props
  541.                     mv.visitInsn Opcodes.DUP
  542.                     mv.visitLdcInsn value
  543.                     @compileAsObject mv, expr
  544.                     mv.visitMethodInsn Opcodes.INVOKEVIRTUAL, mug.qn.object, "set", jvm.sig.call(jvm.sig.obj(jvm.qn.string), jvm.sig.obj(jvm.qn.object), jvm.sig.void)
  545.            
  546.             when 'func-literal'
  547.                 # Find child index.
  548.                 child = input.getContext(n.closure)
  549.                 qn = compiler.qn.context(child.id)
  550.                 # Create context instance.
  551.                 mv.visitTypeInsn Opcodes.NEW, qn
  552.                 mv.visitInsn Opcodes.DUP
  553.                 # Function prototype
  554.                 @compileLoadEnvironment mv
  555.                 # Load scopes.
  556.                 for parent in @ctx.getParents()
  557.                     mv.visitVarInsn Opcodes.ALOAD, 0
  558.                     mv.visitFieldInsn Opcodes.GETFIELD, compiler.qn.context(@ctx.id), "SCOPE_" + parent.id, jvm.sig.obj(compiler.qn.scope(parent.id))
  559.                 mv.visitVarInsn Opcodes.ALOAD, compiler.regs.scope
  560.                 # Invoke.
  561.                 mv.visitMethodInsn Opcodes.INVOKESPECIAL, qn, "<init>",
  562.                     jvm.sig.call jvm.sig.obj(mug.qn.toplevel),
  563.                     (jvm.sig.obj(compiler.qn.scope(ctx.id)) for ctx in @ctx.getLineage())...,
  564.                     jvm.sig.void
  565.  
  566.             # Operations.
  567.  
  568.             when 'add-op-expr'
  569.                 #TODO optimize based on type
  570.                 @compileAsObject mv, n.left
  571.                 @compileAsObject mv, n.right
  572.                 mv.visitMethodInsn Opcodes.INVOKESTATIC, mug.qn.utils, "add", jvm.sig.call(jvm.sig.obj(jvm.qn.object), jvm.sig.obj(jvm.qn.object), jvm.sig.obj(jvm.qn.object))
  573.            
  574.             when 'sub-op-expr'
  575.                 @compileNode mv, n.left
  576.                 @compileAsNumber mv, @getNodeCompileType(n.left)
  577.                 @compileNode mv, n.right
  578.                 @compileAsNumber mv, @getNodeCompileType(n.right)
  579.                 mv.visitInsn Opcodes.DSUB
  580.  
  581.             when 'div-op-expr'
  582.                 @compileNode mv, n.left
  583.                 @compileAsNumber mv, @getNodeCompileType(n.left)
  584.                 @compileNode mv, n.right
  585.                 @compileAsNumber mv, @getNodeCompileType(n.right)
  586.                 mv.visitInsn Opcodes.DDIV
  587.  
  588.             when 'mul-op-expr'
  589.                 @compileNode mv, n.left
  590.                 @compileAsNumber mv, @getNodeCompileType(n.left)
  591.                 @compileNode mv, n.right
  592.                 @compileAsNumber mv, @getNodeCompileType(n.right)
  593.                 mv.visitInsn Opcodes.DMUL
  594.  
  595.             when 'mod-op-expr'
  596.                 @compileNode mv, n.left
  597.                 @compileAsNumber mv, @getNodeCompileType(n.left)
  598.                 @compileNode mv, n.right
  599.                 @compileAsNumber mv, @getNodeCompileType(n.right)
  600.                 mv.visitInsn Opcodes.DREM
  601.  
  602.             when 'lsh-op-expr'
  603.                 @compileNode mv, n.left
  604.                 @compileAsNumber mv, @getNodeCompileType(n.left)
  605.                 mv.visitInsn Opcodes.D2I
  606.                 @compileNode mv, n.right
  607.                 @compileAsNumber mv, @getNodeCompileType(n.right)
  608.                 mv.visitInsn Opcodes.D2I
  609.                 mv.visitInsn Opcodes.ISHL
  610.                 mv.visitInsn Opcodes.I2D
  611.            
  612.             when 'eq-op-expr'
  613.                 @compileAsObject mv, n.left
  614.                 @compileAsObject mv, n.right
  615.                 mv.visitMethodInsn Opcodes.INVOKESTATIC, mug.qn.utils, "testEquality", jvm.sig.call(jvm.sig.obj(jvm.qn.object), jvm.sig.obj(jvm.qn.object), jvm.sig.boolean)
  616.            
  617.             when 'neq-op-expr'
  618.                 @compileNode mv, {
  619.                     type: 'not-op-expr',
  620.                     ln: n.ln, expr: {
  621.                         type: 'eq-op-expr',
  622.                         ln: n.ln, left: n.left, right: n.right
  623.                     } }
  624.            
  625.             when 'not-op-expr'
  626.                 falseCase = new Label(); trueCase = new Label()
  627.                 @compileNode mv, n.expr
  628.                 @compileAsBoolean mv, @getNodeCompileType(n.expr)
  629.                 mv.visitJumpInsn Opcodes.IFNE, falseCase
  630.                 mv.visitInsn Opcodes.ICONST_1
  631.                 mv.visitJumpInsn Opcodes.GOTO, trueCase
  632.                 mv.visitLabel falseCase
  633.                 mv.visitInsn Opcodes.ICONST_0
  634.                 mv.visitLabel trueCase
  635.  
  636.             when 'eqs-op-expr'
  637.                 @compileAsObject mv, n.left
  638.                 @compileAsObject mv, n.right
  639.                 mv.visitMethodInsn Opcodes.INVOKESTATIC, mug.qn.utils, "testStrictEquality", jvm.sig.call(jvm.sig.obj(jvm.qn.object), jvm.sig.obj(jvm.qn.object), jvm.sig.boolean)
  640.            
  641.             when 'neqs-op-expr'
  642.                 @compileNode mv, {
  643.                     type: 'not-op-expr',
  644.                     ln: n.ln, expr: {
  645.                         type: 'eqs-op-expr',
  646.                         ln: n.ln, left: n.left, right: n.right
  647.                     } }
  648.                
  649.             when 'lt-op-expr'
  650.                 @compileComparison mv, Opcodes.IFLT, n.left, n.right
  651.  
  652.             when 'lte-op-expr'
  653.                 @compileComparison mv, Opcodes.IFLE, n.left, n.right
  654.  
  655.             when 'gt-op-expr'
  656.                 @compileComparison mv, Opcodes.IFGT, n.left, n.right
  657.  
  658.             when 'gte-op-expr'
  659.                 @compileComparison mv, Opcodes.IFGE, n.left, n.right
  660.  
  661.             when 'lt-op-expr'
  662.                 @compileComparison mv, Opcodes.IFLT, n.left, n.right
  663.  
  664.             when 'neg-op-expr'
  665.                 @compileNode mv, n.expr
  666.                 @compileAsNumber mv, @getNodeCompileType(n.expr)
  667.                 mv.visitInsn Opcodes.DNEG
  668.            
  669.             when 'or-op-expr'
  670.                 @compileAsObject mv, n.left
  671.                 mv.visitInsn Opcodes.DUP #TODO without this DUP, we could post-facto autobox
  672.                 @compileAsBoolean mv, 'Object'
  673.                 trueCase = new Label()
  674.                 mv.visitJumpInsn Opcodes.IFNE, trueCase
  675.                 mv.visitInsn Opcodes.POP
  676.                 @compileAsObject mv, n.right
  677.                 mv.visitLabel trueCase
  678.            
  679.             when 'and-op-expr'
  680.                 @compileAsObject mv, n.left
  681.                 mv.visitInsn Opcodes.DUP #TODO without this DUP, we could post-facto autobox
  682.                 @compileAsBoolean mv, 'Object'
  683.                 falseCase = new Label()
  684.                 mv.visitJumpInsn Opcodes.IFEQ, falseCase
  685.                 mv.visitInsn Opcodes.POP
  686.                 @compileAsObject mv, n.right
  687.                 mv.visitLabel falseCase
  688.            
  689.             when 'bit-and-op-expr'
  690.                 @compileNode mv, n.left
  691.                 @compileAsNumber mv, @getNodeCompileType(n.left)
  692.                 mv.visitInsn Opcodes.D2L
  693.                 @compileNode mv, n.right
  694.                 @compileAsNumber mv, @getNodeCompileType(n.right)
  695.                 mv.visitInsn Opcodes.D2L
  696.                 mv.visitInsn Opcodes.LAND
  697.                 mv.visitInsn Opcodes.L2D
  698.  
  699.             when 'bit-or-op-expr'
  700.                 @compileNode mv, n.left
  701.                 @compileAsNumber mv, @getNodeCompileType(n.left)
  702.                 mv.visitInsn Opcodes.D2L
  703.                 @compileNode mv, n.right
  704.                 @compileAsNumber mv, @getNodeCompileType(n.right)
  705.                 mv.visitInsn Opcodes.D2L
  706.                 mv.visitInsn Opcodes.LOR
  707.                 mv.visitInsn Opcodes.L2D
  708.  
  709.             when 'bit-xor-op-expr'
  710.                 @compileNode mv, n.left
  711.                 @compileAsNumber mv, @getNodeCompileType(n.left)
  712.                 mv.visitInsn Opcodes.D2L
  713.                 @compileNode mv, n.right
  714.                 @compileAsNumber mv, @getNodeCompileType(n.right)
  715.                 mv.visitInsn Opcodes.D2L
  716.                 mv.visitInsn Opcodes.LXOR
  717.                 mv.visitInsn Opcodes.L2D
  718.            
  719.             when 'instanceof-op-expr'
  720.                 switch @getNodeCompileType(n.left)
  721.                     when 'number', 'boolean'
  722.                         mv.visitInsn Opcodes.ICONST_0
  723.                     else
  724.                         switch @getNodeCompileType(n.right)
  725.                             when 'number', 'boolean', 'String'
  726.                                 throw new Error "Objects cannot be instances of primitives (line #{@ln})"
  727.                             else
  728.                                 @compileAsObject mv, n.right
  729.                                 mv.visitTypeInsn Opcodes.CHECKCAST, mug.qn.object
  730.                                 @compileAsObject mv, n.left
  731.                                 mv.visitMethodInsn Opcodes.INVOKEVIRTUAL, mug.qn.object, "hasInstance", jvm.sig.call(jvm.sig.obj(jvm.qn.object), jvm.sig.boolean)
  732.  
  733.             when 'in-op-expr'
  734.                 throw new Error("TODO: support 'in' operator (line #{@ln}")
  735.  
  736.             when 'typeof-op-expr'
  737.                 @compileNode mv, n.expr
  738.                 mv.visitMethodInsn Opcodes.INVOKESTATIC, mug.qn.utils, "typeof", jvm.sig.call(jvm.sig.obj(jvm.qn.object), jvm.sig.obj(jvm.qn.string))
  739.            
  740.             when 'void-op-expr'
  741.                 @compileNode mv, n.expr
  742.                 @compilePop mv, n.expr
  743.                 mv.visitInsn Opcodes.ACONST_NULL # undefined
  744.            
  745.             when 'seq-op-expr'
  746.                 @compileNode mv, n.left
  747.                 @compilePop mv, n.left
  748.                 @compileNode mv, n.right
  749.  
  750.             # Expressions.
  751.  
  752.             when 'this-expr'
  753.                 mv.visitVarInsn Opcodes.ALOAD, 1
  754.            
  755.             when 'if-expr'
  756.                 @compileNode mv, n.expr
  757.                 @compileAsBoolean mv, @getNodeCompileType(n.expr)
  758.                
  759.                 falseCase = new Label(); trueCase = new Label()
  760.                 mv.visitJumpInsn Opcodes.IFEQ, falseCase
  761.                 @compileAsObject mv, n.thenExpr
  762.                 mv.visitJumpInsn Opcodes.GOTO, trueCase
  763.                 mv.visitLabel falseCase
  764.                 @compileAsObject mv, n.elseExpr
  765.                 mv.visitLabel trueCase
  766.  
  767.             when 'scope-ref-expr'
  768.                 if (reg = @getRegister(n.value)) != -1
  769.                     mv.visitVarInsn Opcodes.ALOAD, reg
  770.                 else
  771.                     qn_parentC = @compileScopeRef mv, n.value, n.ln
  772.                     mv.visitMethodInsn Opcodes.INVOKEVIRTUAL, qn_parentC, "get_" + n.value, jvm.sig.call(jvm.sig.obj(jvm.qn.object))
  773.  
  774.             when 'static-ref-expr'
  775.                 @compileJSObject mv, n.base
  776.                 mv.visitLdcInsn n.value
  777.                 mv.visitMethodInsn Opcodes.INVOKEVIRTUAL, mug.qn.object, "get", jvm.sig.call(jvm.sig.obj(jvm.qn.string), jvm.sig.obj(jvm.qn.object))
  778.            
  779.             when 'dyn-ref-expr'
  780.                 @compileJSObject mv, n.base
  781.                 switch @getNodeCompileType(n.index)
  782.                     when 'number'
  783.                         #TODO this isn't valid for non-integers!!!
  784.                         @compileNode mv, n.index
  785.                         mv.visitInsn Opcodes.D2I
  786.                         mv.visitMethodInsn Opcodes.INVOKEVIRTUAL, mug.qn.object, "get", jvm.sig.call(jvm.sig.integer, jvm.sig.obj(jvm.qn.object))
  787.                     else
  788.                         @compileAsObject mv, n.index
  789.                         mv.visitMethodInsn Opcodes.INVOKEVIRTUAL, mug.qn.object, "get", jvm.sig.call(jvm.sig.obj(jvm.qn.object), jvm.sig.obj(jvm.qn.object))
  790.            
  791.             when 'static-method-call-expr'
  792.                 # get argument and method
  793.                 @compileJSObject mv, n.base
  794.                 mv.visitInsn Opcodes.DUP
  795.                 mv.visitLdcInsn n.value
  796.                 mv.visitMethodInsn Opcodes.INVOKEVIRTUAL, mug.qn.object, "get", jvm.sig.call(jvm.sig.obj(jvm.qn.string), jvm.sig.obj(jvm.qn.object))
  797.                 mv.visitTypeInsn Opcodes.CHECKCAST, mug.qn.object
  798.                 mv.visitInsn Opcodes.SWAP
  799.                 @compileInvokeArgs mv, n.args
  800.                 mv.visitMethodInsn Opcodes.INVOKEVIRTUAL, mug.qn.object, "invoke", compiler.sig.invoke()
  801.  
  802.             when 'call-expr'
  803.                 @compileJSObject mv, n.expr
  804.                 mv.visitInsn Opcodes.ACONST_NULL # "this"
  805.                 @compileInvokeArgs mv, n.args
  806.                 mv.visitMethodInsn Opcodes.INVOKEVIRTUAL, mug.qn.object, "invoke", compiler.sig.invoke()
  807.            
  808.             when 'new-expr'
  809.                 @compileNode mv, n.constructor
  810.                 mv.visitTypeInsn Opcodes.CHECKCAST, mug.qn.object
  811.                 @compileInvokeArgs mv, n.args
  812.                 mv.visitMethodInsn Opcodes.INVOKEVIRTUAL, mug.qn.object, "instantiate", compiler.sig.instantiate
  813.  
  814.             #TODO
  815.             # we should be able to compile assignments without autoboxing the resulting
  816.             # value, so that we can gain from type analysis
  817.             when 'scope-assign-expr'
  818.                 @compileAsObject mv, n.expr
  819.                 mv.visitInsn Opcodes.DUP
  820.                 if (reg = @getRegister(n.value)) != -1
  821.                     mv.visitVarInsn Opcodes.ASTORE, reg
  822.                 else
  823.                     qn_parentD = @compileScopeRef mv, n.value, n.ln
  824.                     mv.visitInsn Opcodes.SWAP
  825.                     mv.visitMethodInsn Opcodes.INVOKEVIRTUAL, qn_parentD, "set_" + n.value, jvm.sig.call(jvm.sig.obj(jvm.qn.object), jvm.sig.void)
  826.            
  827.             when 'static-assign-expr'
  828.                 @compileJSObject mv, n.base
  829.                 mv.visitLdcInsn n.value
  830.                 @compileAsObject mv, n.expr
  831.                 mv.visitInsn Opcodes.DUP_X2
  832.                 mv.visitMethodInsn Opcodes.INVOKEVIRTUAL, mug.qn.object, "set", jvm.sig.call(jvm.sig.obj(jvm.qn.string), jvm.sig.obj(jvm.qn.object), jvm.sig.void)
  833.            
  834.             when 'dyn-assign-expr'
  835.                 # mv.visitInsn Opcodes.DUP
  836.                 @compileJSObject mv, n.base
  837.                 # mv.visitInsn Opcodes.SWAP
  838.                 switch @getNodeCompileType(n.index)
  839.                     #TODO this isn't valid for non-integers!!!
  840.                     when 'number'
  841.                         @compileNode mv, n.index
  842.                         mv.visitInsn Opcodes.D2I
  843.                         @compileAsObject mv, n.expr
  844.                         mv.visitInsn Opcodes.DUP_X2
  845.                         mv.visitMethodInsn Opcodes.INVOKEVIRTUAL, mug.qn.object, "set", jvm.sig.call(jvm.sig.integer, jvm.sig.obj(jvm.qn.object), jvm.sig.void)
  846.  
  847.                     else
  848.                         @compileAsObject mv, n.index
  849.                         @compileAsObject mv, n.expr
  850.                         mv.visitInsn Opcodes.DUP_X2
  851.                         mv.visitMethodInsn Opcodes.INVOKEVIRTUAL, mug.qn.object, "set", jvm.sig.call(jvm.sig.obj(jvm.qn.object), jvm.sig.obj(jvm.qn.object), jvm.sig.void)
  852.            
  853.             when 'scope-inc-expr'
  854.                 if n.pre
  855.                     n2 =
  856.                         type: 'scope-assign-expr'
  857.                         ln: n.ln
  858.                         value: n.value
  859.                         expr:
  860.                             type: 'add-op-expr'
  861.                             ln: n.ln
  862.                             left:
  863.                                 type: 'scope-ref-expr'
  864.                                 ln: n.ln
  865.                                 value: n.value
  866.                             right:
  867.                                 type: 'num-literal'
  868.                                 ln: n.ln
  869.                                 value: n.inc
  870.                     @compileNode mv, n2
  871.                     @compileAsNumber mv, @getNodeCompileType(n2)
  872.  
  873.                 else
  874.                     if (reg = @getRegister(n.value)) != -1
  875.                         mv.visitVarInsn Opcodes.ALOAD, reg
  876.                         # {var}
  877.                         @compileAsNumber mv, 'Object'
  878.                         # {double|value}
  879.                         mv.visitInsn Opcodes.DUP2
  880.                         # {double|value} {double|value}
  881.                     else
  882.                         qn_parentE = @compileScopeRef mv, n.value, n.ln
  883.                         # {scope}
  884.                         mv.visitInsn Opcodes.DUP
  885.                         # {scope} {scope}
  886.                         mv.visitMethodInsn Opcodes.INVOKEVIRTUAL, qn_parentE, "get_" + n.value, jvm.sig.call(jvm.sig.obj(jvm.qn.object))
  887.                         # {scope} {var}
  888.                         @compileAsNumber mv, 'Object'
  889.                         # {scope} {double|value}
  890.                         mv.visitInsn Opcodes.DUP2_X1
  891.                         # {double|value} {scope} {double|value}
  892.                     mv.visitTypeInsn Opcodes.NEW, jvm.qn.double
  893.                     # ... {double|value} {obj}
  894.                     mv.visitInsn Opcodes.DUP_X2
  895.                     # ... {obj} {double|value} {obj}
  896.                     mv.visitInsn Opcodes.DUP_X2
  897.                     # ... {obj} {obj} {double|value} {obj}
  898.                     mv.visitInsn Opcodes.POP
  899.                     # ... {obj} {obj} {double|value}
  900.                     mv.visitLdcInsn new java.lang.Double(n.inc)
  901.                     # ... {obj} {obj} {double|value} {double|1.0}
  902.                     mv.visitInsn Opcodes.DADD
  903.                     # ... {obj} {obj} {double|value+1}
  904.                     mv.visitMethodInsn Opcodes.INVOKESPECIAL, jvm.qn.double, "<init>", jvm.sig.call(jvm.sig.double, jvm.sig.void)
  905.                     # ... {obj}
  906.                     if reg != -1
  907.                         mv.visitVarInsn Opcodes.ASTORE, reg
  908.                     else
  909.                         mv.visitMethodInsn Opcodes.INVOKEVIRTUAL, qn_parentE, "set_" + n.value, jvm.sig.call(jvm.sig.obj(jvm.qn.object), jvm.sig.void)
  910.                     # {double|value}
  911.  
  912.             when 'static-inc-expr'
  913.                 if n.pre
  914.                     #TODO this doesn't really work, need to save refs!
  915.                     n2 =
  916.                         type: 'static-assign-expr'
  917.                         ln: n.ln
  918.                         base: n.base
  919.                         value: n.value
  920.                         expr:
  921.                             type: 'add-op-expr'
  922.                             ln: n.ln
  923.                             left:
  924.                                 type: 'static-ref-expr'
  925.                                 ln: n.ln
  926.                                 base: n.base
  927.                                 value: n.value
  928.                             right:
  929.                                 type: 'num-literal'
  930.                                 ln: n.ln
  931.                                 value: n.inc
  932.                     @compileNode mv, n2
  933.                     @compileAsNumber mv, @getNodeCompileType(n2)
  934.  
  935.                 else
  936.                     @compileJSObject mv, n.base
  937.                     # {base}
  938.                     mv.visitInsn Opcodes.DUP
  939.                     # {base} {base}
  940.                     mv.visitLdcInsn n.value
  941.                     mv.visitMethodInsn Opcodes.INVOKEVIRTUAL, mug.qn.object, "get", jvm.sig.call(jvm.sig.obj(jvm.qn.string), jvm.sig.obj(jvm.qn.object))
  942.                     # {base} {var}
  943.                     @compileAsNumber mv, 'Object'
  944.                     # {base} {double|value}
  945.                     mv.visitInsn Opcodes.DUP2_X1
  946.                     # {double|value} {base} {double|value}
  947.                     mv.visitTypeInsn Opcodes.NEW, jvm.qn.double
  948.                     # {double|value} {scope} {double|value} {obj}
  949.                     mv.visitInsn Opcodes.DUP_X2
  950.                     # {double|value} {scope} {obj} {double|value} {obj}
  951.                     mv.visitInsn Opcodes.DUP_X2
  952.                     # {double|value} {scope} {obj} {obj} {double|value} {obj}
  953.                     mv.visitInsn Opcodes.POP
  954.                     # {double|value} {scope} {obj} {obj} {double|value}
  955.                     mv.visitLdcInsn new java.lang.Double(n.inc)
  956.                     # {double|value} {scope} {obj} {obj} {double|value} {double|1.0}
  957.                     mv.visitInsn Opcodes.DADD
  958.                     # {double|value} {scope} {obj} {obj} {double|value+1}
  959.                     mv.visitMethodInsn Opcodes.INVOKESPECIAL, jvm.qn.double, "<init>", jvm.sig.call(jvm.sig.double, jvm.sig.void)
  960.                     # {double|value} {scope} {obj}
  961.                     mv.visitLdcInsn n.value
  962.                     mv.visitInsn Opcodes.SWAP
  963.                     mv.visitMethodInsn Opcodes.INVOKEVIRTUAL, mug.qn.object, "set", jvm.sig.call(jvm.sig.obj(jvm.qn.string), jvm.sig.obj(jvm.qn.object), jvm.sig.void)
  964.                     # {double|value}
  965.  
  966.             when 'dyn-inc-expr'
  967.                 if true
  968.                     #TODO this doesn't really work, need to save refs!
  969.                     n2 =
  970.                         type: 'dyn-assign-expr'
  971.                         ln: n.ln
  972.                         base: n.base
  973.                         index: n.index
  974.                         expr:
  975.                             type: 'add-op-expr'
  976.                             ln: n.ln
  977.                             left:
  978.                                 type: 'dyn-ref-expr'
  979.                                 ln: n.ln
  980.                                 base: n.base
  981.                                 index: n.index
  982.                             right:
  983.                                 type: 'num-literal'
  984.                                 ln: n.ln
  985.                                 value: n.inc
  986.                     @compileNode mv, n2
  987.                     @compileAsNumber mv, @getNodeCompileType(n2)
  988.                 else
  989.                     throw new Error 'No support for dyn-inc-expr(pre)'
  990.                
  991.             when 'scope-delete-expr'
  992.                 #TODO could allow deletion of global (scope) variables
  993.                 mv.visitInsn Opcodes.ICONST_0
  994.            
  995.             when 'static-delete-expr'
  996.                 @compileJSObject mv, n.base
  997.                 mv.visitLdcInsn n.value
  998.                 mv.visitMethodInsn Opcodes.INVOKEVIRTUAL, mug.qn.object, "delete", jvm.sig.call(jvm.sig.obj(jvm.qn.string), jvm.sig.boolean)
  999.            
  1000.             when 'dyn-delete-expr'
  1001.                 @compileJSObject mv, n.base
  1002.                 @compileAsObject mv, n.index
  1003.                 mv.visitMethodInsn Opcodes.INVOKEVIRTUAL, mug.qn.object, "delete", jvm.sig.call(jvm.sig.obj(jvm.qn.object), jvm.sig.boolean)
  1004.  
  1005.             # Statements.
  1006.  
  1007.             when 'block-stat'
  1008.                 (@compileNode mv, stat) for stat in n.stats
  1009.            
  1010.             when 'expr-stat'
  1011.                 @compileNode mv, n.expr
  1012.                 @compilePop mv, n.expr
  1013.  
  1014.             when 'ret-stat'
  1015.                 unless n.expr
  1016.                     mv.visitInsn Opcodes.ACONST_NULL
  1017.                 else
  1018.                     @compileAsObject mv, n.expr
  1019.                 mv.visitInsn Opcodes.ARETURN
  1020.            
  1021.             when 'while-stat'
  1022.                 trueCase = new Label(); falseCase = new Label()
  1023.                 @pushLabel null, trueCase, falseCase
  1024.  
  1025.                 mv.visitLabel trueCase
  1026.                 @compileNode mv, n.expr
  1027.                 @compileAsBoolean mv, @getNodeCompileType(n.expr)
  1028.                 mv.visitJumpInsn Opcodes.IFEQ, falseCase
  1029.                 if n.stat
  1030.                     @compileNode mv, n.stat
  1031.                 mv.visitJumpInsn Opcodes.GOTO, trueCase
  1032.                 mv.visitLabel falseCase
  1033.  
  1034.                 @popLabel null
  1035.            
  1036.             when 'do-while-stat'
  1037.                 trueCase = new Label(); falseCase = new Label()
  1038.                 @pushLabel null, trueCase, falseCase
  1039.  
  1040.                 mv.visitLabel trueCase
  1041.                 if n.stat
  1042.                     @compileNode mv, n.stat
  1043.                 @compileNode mv, n.expr
  1044.                 @compileAsBoolean mv, @getNodeCompileType(n.expr)
  1045.                 mv.visitJumpInsn Opcodes.IFNE, trueCase
  1046.                 mv.visitLabel falseCase
  1047.  
  1048.                 @popLabel null
  1049.            
  1050.             when 'for-stat'
  1051.                 if n.init
  1052.                     @compileNode mv, n.init
  1053.                     if jast.isExpr(n.init)
  1054.                         @compilePop mv, n.init
  1055.                
  1056.                 startLabel = new Label(); continueLabel = new Label(); breakLabel = new Label()
  1057.                 @pushLabel null, continueLabel, breakLabel
  1058.  
  1059.                 mv.visitLabel startLabel
  1060.                 if n.expr
  1061.                     @compileNode mv, n.expr
  1062.                     @compileAsBoolean mv, @getNodeCompileType(n.expr)
  1063.                     mv.visitJumpInsn Opcodes.IFEQ, breakLabel
  1064.                 if n.stat
  1065.                     @compileNode mv, n.stat
  1066.                 mv.visitLabel continueLabel
  1067.                 if n.step
  1068.                     @compileNode mv, n.step
  1069.                     @compilePop mv, n.step
  1070.                 mv.visitJumpInsn Opcodes.GOTO, startLabel
  1071.                 mv.visitLabel breakLabel
  1072.  
  1073.                 @popLabel null
  1074.  
  1075.             when 'if-stat'
  1076.                 @compileNode mv, n.expr
  1077.                 @compileAsBoolean mv, @getNodeCompileType(n.expr)
  1078.                 falseCase = new Label(); trueCase = new Label()
  1079.                 mv.visitJumpInsn Opcodes.IFEQ, falseCase
  1080.                 @compileNode mv, n.thenStat
  1081.                 mv.visitJumpInsn Opcodes.GOTO, trueCase
  1082.                 mv.visitLabel falseCase
  1083.                 if n.elseStat
  1084.                     @compileNode mv, n.elseStat
  1085.                 mv.visitLabel trueCase
  1086.            
  1087.             when 'switch-stat'
  1088.                 endCase = new Label(); labels = (new Label for _ in n.cases)
  1089.                 @pushLabel null, null, endCase
  1090.  
  1091.                 @compileAsObject mv, n.expr
  1092.                 # Test for cases.
  1093.                 for {match, stat}, i in n.cases when match
  1094.                     mv.visitInsn Opcodes.DUP
  1095.                     @compileAsObject mv, match
  1096.                     mv.visitMethodInsn Opcodes.INVOKESTATIC, mug.qn.utils, "testEquality", jvm.sig.call(jvm.sig.obj(jvm.qn.object), jvm.sig.obj(jvm.qn.object), jvm.sig.boolean)
  1097.                     mv.visitJumpInsn Opcodes.IFNE, labels[i]
  1098.                 # Default cases.
  1099.                 for {match, stat}, i in n.cases when not match
  1100.                     mv.visitJumpInsn Opcodes.GOTO, labels[i]
  1101.                 mv.visitJumpInsn Opcodes.GOTO, endCase
  1102.  
  1103.                 # Write out clauses.
  1104.                 for {match, stat}, i in n.cases
  1105.                     mv.visitLabel labels[i]
  1106.                     @compileNode mv, stat
  1107.                
  1108.                 mv.visitLabel endCase
  1109.                 mv.visitInsn Opcodes.POP # original switch expression
  1110.  
  1111.                 @popLabel null
  1112.            
  1113.             when 'throw-stat'
  1114.                 mv.visitTypeInsn Opcodes.NEW, mug.qn.valueException
  1115.                 mv.visitInsn Opcodes.DUP
  1116.                 @compileAsObject mv, n.expr
  1117.                 mv.visitMethodInsn Opcodes.INVOKESPECIAL, mug.qn.valueException, "<init>", jvm.sig.call(jvm.sig.obj(jvm.qn.object), jvm.sig.void)
  1118.                 mv.visitInsn Opcodes.ATHROW
  1119.            
  1120.             when 'try-stat'
  1121.                 # Holy mother is this one long
  1122.                 tryLabel = new Label; catchLabel = new Label; finallyLabel = new Label
  1123.                 doubleThrowLabel = new Label; endLabel = new Label
  1124.  
  1125.                 # Try.
  1126.                 mv.visitLabel tryLabel
  1127.                 @compileNode mv, n.tryStat
  1128.                 mv.visitJumpInsn Opcodes.GOTO, finallyLabel
  1129.  
  1130.                 # Catch (body may be empty)
  1131.                 mv.visitLabel catchLabel
  1132.                 if n.catchBlock
  1133.                     exceptionLabel = new Label; catchEndLabel = new Label
  1134.  
  1135.                     # Unwrap JSValueExceptions
  1136.                     mv.visitInsn Opcodes.DUP
  1137.                     mv.visitTypeInsn Opcodes.INSTANCEOF, mug.qn.valueException
  1138.                     mv.visitJumpInsn Opcodes.IFEQ, exceptionLabel
  1139.                     mv.visitTypeInsn Opcodes.CHECKCAST, mug.qn.valueException
  1140.                     mv.visitFieldInsn Opcodes.GETFIELD, mug.qn.valueException, "value", jvm.sig.obj(jvm.qn.object)
  1141.                     mv.visitJumpInsn Opcodes.GOTO, catchEndLabel
  1142.                     # ...and wrap non-JS exceptions.
  1143.                     mv.visitLabel exceptionLabel
  1144.                     mv.visitInsn Opcodes.DUP
  1145.                     mv.visitTypeInsn Opcodes.INSTANCEOF, mug.qn.exception
  1146.                     mv.visitJumpInsn Opcodes.IFNE, catchEndLabel
  1147.                     mv.visitTypeInsn Opcodes.NEW, mug.qn.exception
  1148.                     mv.visitInsn Opcodes.DUP_X1
  1149.                     mv.visitInsn Opcodes.SWAP
  1150.                     @compileLoadEnvironment mv
  1151.                     mv.visitInsn Opcodes.SWAP
  1152.                     mv.visitMethodInsn Opcodes.INVOKESPECIAL, mug.qn.exception, "<init>", jvm.sig.call(jvm.sig.obj(mug.qn.toplevel), jvm.sig.obj(jvm.qn.exception), jvm.sig.void)
  1153.                     # Done with check.
  1154.                     mv.visitLabel catchEndLabel
  1155.  
  1156.                     # Assign variable.
  1157.                     if (reg = @getRegister(n.catchBlock.value)) != -1
  1158.                         mv.visitVarInsn Opcodes.ASTORE, reg
  1159.                     else
  1160.                         qn_parentA = @compileScopeRef mv, n.catchBlock.value, n.catchBlock.ln
  1161.                         mv.visitInsn Opcodes.SWAP
  1162.                         mv.visitMethodInsn Opcodes.INVOKEVIRTUAL, qn_parentA, "set_" + n.catchBlock.value, jvm.sig.call(jvm.sig.obj(jvm.qn.object), jvm.sig.void)
  1163.  
  1164.                     # Body
  1165.                     @compileNode mv, n.catchBlock.stat if n.catchBlock.stat
  1166.                 else
  1167.                     mv.visitInsn Opcodes.POP
  1168.                
  1169.                 # Finally (optional)
  1170.                 mv.visitLabel finallyLabel
  1171.                 if n.finallyStat
  1172.                     @compileNode mv, n.finallyStat
  1173.                 mv.visitJumpInsn Opcodes.GOTO, endLabel
  1174.  
  1175.                 # Throws inside catch (duplicates finally body)
  1176.                 mv.visitLabel doubleThrowLabel
  1177.                 if n.finallyStat
  1178.                     @compileNode mv, n.finallyStat
  1179.                 mv.visitInsn Opcodes.ATHROW
  1180.  
  1181.                 mv.visitLabel endLabel
  1182.  
  1183.                 # Catch block
  1184.                 mv.visitTryCatchBlock tryLabel, catchLabel, catchLabel, jvm.qn.exception
  1185.                 # Finally block.
  1186.                 mv.visitTryCatchBlock tryLabel, finallyLabel, doubleThrowLabel, null
  1187.  
  1188.             when 'break-stat'
  1189.                 unless (label = @getLabel null)
  1190.                     throw new Error "Cannot break outside of loop"
  1191.                 mv.visitJumpInsn Opcodes.GOTO, label.brk
  1192.  
  1193.             when 'continue-stat'
  1194.                 unless (label = @getLabel null)
  1195.                     throw new Error "Cannot continue outside of loop"
  1196.                 mv.visitJumpInsn Opcodes.GOTO, label.cont
  1197.            
  1198.             when 'var-stat'
  1199.                 for {value, expr} in n.vars when expr
  1200.                     @compileNode mv,
  1201.                         type: 'expr-stat'
  1202.                         ln: expr.ln
  1203.                         expr:
  1204.                             type: 'scope-assign-expr'
  1205.                             ln: expr.ln
  1206.                             value: value
  1207.                             expr: expr
  1208.  
  1209.             when 'defn-stat'
  1210.                 @compileNode mv, {
  1211.                     type: 'expr-stat'
  1212.                     ln: n.ln
  1213.                     expr: {
  1214.                         type: 'scope-assign-expr'
  1215.                         ln: n.ln
  1216.                         value: n.closure.name
  1217.                         expr: {type: 'func-literal', ln: n.ln, closure: n.closure}
  1218.                     }}
  1219.            
  1220.             when 'for-in-stat'
  1221.                 checkLabel = new Label(); statLabel = new Label()
  1222.                 @pushLabel null, statLabel, checkLabel
  1223.  
  1224.                 @compileJSObject mv, n.expr
  1225.                 mv.visitMethodInsn Opcodes.INVOKEVIRTUAL, mug.qn.object, "getKeys", jvm.sig.call(jvm.sig.array(jvm.sig.obj(jvm.qn.string)))
  1226.                 mv.visitInsn Opcodes.ICONST_0
  1227.                 mv.visitJumpInsn Opcodes.GOTO, checkLabel
  1228.                 mv.visitLabel statLabel
  1229.                 # load from array
  1230.                 mv.visitInsn Opcodes.DUP2
  1231.                 mv.visitInsn Opcodes.AALOAD
  1232.                 # store in scope
  1233.                 if (reg = @getRegister(n.value)) != -1
  1234.                     mv.visitVarInsn Opcodes.ASTORE, reg
  1235.                 else
  1236.                     qn_parentB = @compileScopeRef mv, n.value, n.ln
  1237.                     mv.visitInsn Opcodes.SWAP
  1238.                     mv.visitMethodInsn Opcodes.INVOKEVIRTUAL, qn_parentB, "set_" + n.value, jvm.sig.call(jvm.sig.obj(jvm.qn.object), jvm.sig.void)
  1239.  
  1240.                 @compileNode mv, n.stat
  1241.  
  1242.                 mv.visitInsn Opcodes.ICONST_1
  1243.                 mv.visitInsn Opcodes.IADD
  1244.                 mv.visitLabel checkLabel
  1245.                 mv.visitInsn Opcodes.DUP2
  1246.                 mv.visitInsn Opcodes.SWAP
  1247.                 mv.visitInsn Opcodes.ARRAYLENGTH
  1248.                 mv.visitJumpInsn Opcodes.IF_ICMPLT, statLabel
  1249.                 mv.visitInsn Opcodes.POP2
  1250.  
  1251.                 @popLabel null
  1252.  
  1253.             # Fallback.
  1254.  
  1255.             else
  1256.                 throw new Error 'Unrecognized type ' + JSON.stringify(n.type)
  1257.        
  1258.         return
  1259.  
  1260. #######################################################################
  1261. # contexts
  1262. #######################################################################
  1263.  
  1264. class ContextCompiler
  1265.    
  1266.     constructor: (@path, @ctx) ->
  1267.  
  1268.     compileScopeClass: ->
  1269.         # Initialize class.
  1270.         qn = compiler.qn.scope(@ctx.id)
  1271.         scope = @ctx.localVars.slice() # todo filter out variables stored locally in registers (analyze.clj)
  1272.         if @ctx.id == 0
  1273.             scope.push(x) for x in compiler.globals when x not in scope
  1274.         cw = new ClassWriter(ClassWriter.COMPUTE_MAXS)
  1275.         cw.visit Opcodes.V1_6, Opcodes.ACC_SUPER + Opcodes.ACC_PUBLIC, qn, null, jvm.qn.object, null
  1276.  
  1277.         # Compile fields.hahha
  1278.         for name in scope
  1279.             cw.visitField(0, "_" + name, jvm.sig.obj(jvm.qn.object), null, null).visitEnd()
  1280.        
  1281.         # Compile methods.
  1282.         for name in scope
  1283.             mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "get_" + name, jvm.sig.call(jvm.sig.obj(jvm.qn.object)), null, null)
  1284.             mv.visitCode()
  1285.             mv.visitVarInsn Opcodes.ALOAD, 0
  1286.             mv.visitFieldInsn Opcodes.GETFIELD, qn, "_" + name, jvm.sig.obj(jvm.qn.object)
  1287.             mv.visitInsn Opcodes.ARETURN
  1288.             mv.visitMaxs 1, 1
  1289.             mv.visitEnd()
  1290.  
  1291.             mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "set_" + name, jvm.sig.call(jvm.sig.obj(jvm.qn.object), jvm.sig.void), null, null)
  1292.             mv.visitCode()
  1293.             mv.visitVarInsn Opcodes.ALOAD, 0
  1294.             mv.visitVarInsn Opcodes.ALOAD, 1
  1295.             mv.visitFieldInsn Opcodes.PUTFIELD, qn, "_" + name, jvm.sig.obj(jvm.qn.object)
  1296.             mv.visitInsn Opcodes.RETURN
  1297.             mv.visitMaxs 2, 2
  1298.             mv.visitEnd()
  1299.  
  1300.         # Write initializer.
  1301.         if @ctx.id == 0
  1302.             mv = cw.visitMethod Opcodes.ACC_PUBLIC, "<init>", jvm.sig.call(jvm.sig.obj(mug.qn.toplevel), jvm.sig.void), null, null
  1303.             mv.visitCode()
  1304.             mv.visitVarInsn Opcodes.ALOAD, 0
  1305.             mv.visitMethodInsn Opcodes.INVOKESPECIAL, jvm.qn.object, "<init>", jvm.sig.call(jvm.sig.void)
  1306.  
  1307.             # load some props
  1308.             for name in compiler.globals when name != 'exports'
  1309.                 mv.visitVarInsn Opcodes.ALOAD, 0
  1310.                 mv.visitVarInsn Opcodes.ALOAD, 1
  1311.                 mv.visitMethodInsn Opcodes.INVOKEVIRTUAL, mug.qn.toplevel, "get_" + name, jvm.sig.call(jvm.sig.obj(jvm.qn.object))
  1312.                 mv.visitFieldInsn Opcodes.PUTFIELD, qn, "_" + name, jvm.sig.obj(jvm.qn.object)
  1313.  
  1314.             mv.visitInsn Opcodes.RETURN
  1315.             mv.visitMaxs 1, 1
  1316.             mv.visitEnd()
  1317.         else
  1318.             mv = cw.visitMethod Opcodes.ACC_PUBLIC, "<init>", jvm.sig.call(jvm.sig.void), null, null
  1319.             mv.visitCode()
  1320.             mv.visitVarInsn Opcodes.ALOAD, 0
  1321.             mv.visitMethodInsn Opcodes.INVOKESPECIAL, jvm.qn.object, "<init>", jvm.sig.call(jvm.sig.void)
  1322.             mv.visitInsn Opcodes.RETURN
  1323.             mv.visitMaxs 1, 1
  1324.             mv.visitEnd()
  1325.        
  1326.         # Finalize class.
  1327.         cw.visitEnd()
  1328.         log.verbose '...Compiled scope ' + qn
  1329.         return [qn + '.class', cw.toByteArray()]
  1330.  
  1331. class ScriptContextCompiler extends ContextCompiler
  1332.  
  1333.     compileClass: ->
  1334.         cw = new ClassWriter(ClassWriter.COMPUTE_MAXS)
  1335.         qn = compiler.qn.context(@ctx.id)
  1336.         cw.visit Opcodes.V1_6, Opcodes.ACC_SUPER + Opcodes.ACC_PUBLIC, qn, null, mug.qn.module, null
  1337.         cw.visitSource @path, null
  1338.  
  1339.         @compileInit cw
  1340.         @compileMethods cw
  1341.         @compileFields cw
  1342.  
  1343.         # Main.
  1344.         mv = cw.visitMethod Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "main", jvm.sig.call(jvm.sig.array(jvm.sig.obj(jvm.qn.string)), jvm.sig.void), null, compiler.stringArray(jvm.qn.exception)
  1345.         mv.visitCode()
  1346.         mv.visitTypeInsn Opcodes.NEW, compiler.qn.script
  1347.         mv.visitInsn Opcodes.DUP
  1348.         mv.visitMethodInsn Opcodes.INVOKESPECIAL, compiler.qn.script, "<init>", jvm.sig.call(jvm.sig.void)
  1349.         mv.visitTypeInsn Opcodes.NEW, mug.qn.toplevel
  1350.         mv.visitInsn Opcodes.DUP
  1351.         mv.visitVarInsn Opcodes.ALOAD, 0
  1352.         mv.visitMethodInsn Opcodes.INVOKESPECIAL, mug.qn.toplevel, "<init>", jvm.sig.call(jvm.sig.array(jvm.sig.obj(jvm.qn.string)), jvm.sig.void)
  1353.         mv.visitMethodInsn Opcodes.INVOKEVIRTUAL, compiler.qn.script, "load", jvm.sig.call(jvm.sig.obj(mug.qn.toplevel), jvm.sig.obj(mug.qn.object))
  1354.         mv.visitInsn Opcodes.POP
  1355.  
  1356.         # Wait for timeouts.
  1357.         mv.visitMethodInsn Opcodes.INVOKESTATIC, mug.qn.timers, "awaitTaskPool", jvm.sig.call(jvm.sig.void)
  1358.  
  1359.         mv.visitInsn Opcodes.RETURN
  1360.         mv.visitMaxs 1, 1
  1361.         mv.visitEnd
  1362.  
  1363.         # finalize
  1364.         cw.visitEnd()
  1365.         log.verbose '...Compiled script ' + qn
  1366.         return [qn + '.class', cw.toByteArray()]
  1367.  
  1368.     compileInit: (cw) ->
  1369.         sig = jvm.sig.call(jvm.sig.void)
  1370.         mv = cw.visitMethod Opcodes.ACC_PUBLIC, "<init>", sig, null, null
  1371.  
  1372.         mv.visitCode()
  1373.         mv.visitVarInsn Opcodes.ALOAD, 0
  1374.         mv.visitMethodInsn Opcodes.INVOKESPECIAL, mug.qn.module, "<init>", sig
  1375.         mv.visitInsn Opcodes.RETURN
  1376.         mv.visitMaxs 1, 1
  1377.         mv.visitEnd()
  1378.         return
  1379.  
  1380.     compileFields: (cw) ->
  1381.         cw.visitField(0, compiler.ident.env, jvm.sig.obj(mug.qn.toplevel), null, null).visitEnd()
  1382.         return
  1383.  
  1384.     compileMethods: (cw) ->
  1385.         mv = cw.visitMethod Opcodes.ACC_PUBLIC, "load", jvm.sig.call(jvm.sig.obj(mug.qn.toplevel), jvm.sig.obj(mug.qn.object)), null, compiler.stringArray(jvm.qn.exception)
  1386.  
  1387.         # start method compiler
  1388.         asm = new MethodCompiler @ctx
  1389.  
  1390.         # Create scope and store in register.
  1391.         mv.visitTypeInsn Opcodes.NEW, compiler.qn.scriptscope()
  1392.         mv.visitInsn Opcodes.DUP
  1393.         mv.visitVarInsn Opcodes.ALOAD, 1 # ENV
  1394.         mv.visitMethodInsn Opcodes.INVOKESPECIAL, compiler.qn.scriptscope(), "<init>", jvm.sig.call(jvm.sig.obj(mug.qn.toplevel), jvm.sig.void)
  1395.         mv.visitVarInsn Opcodes.ASTORE, compiler.regs.scope
  1396.         # Create exports object.
  1397.         mv.visitTypeInsn Opcodes.NEW, mug.qn.object
  1398.         mv.visitInsn Opcodes.DUP
  1399.         mv.visitVarInsn Opcodes.ALOAD, 1 # ENV
  1400.         mv.visitMethodInsn Opcodes.INVOKESPECIAL, mug.qn.object, "<init>", jvm.sig.call(jvm.sig.obj(mug.qn.toplevel), jvm.sig.void)
  1401.         # Store in scope.
  1402.         mv.visitInsn Opcodes.DUP
  1403.         mv.visitVarInsn Opcodes.ALOAD, compiler.regs.scope
  1404.         mv.visitInsn Opcodes.SWAP
  1405.         mv.visitMethodInsn Opcodes.INVOKEVIRTUAL, compiler.qn.scriptscope(), "set_exports", jvm.sig.call(jvm.sig.obj(jvm.qn.object), jvm.sig.void)
  1406.         # Cache environment as property....
  1407.         mv.visitVarInsn Opcodes.ALOAD, 0
  1408.         mv.visitVarInsn Opcodes.ALOAD, 1
  1409.         mv.visitFieldInsn Opcodes.PUTFIELD, compiler.qn.context(@ctx.id), compiler.ident.env, jvm.sig.obj(mug.qn.toplevel)
  1410.         # Store exports object as "this" object (register 1)
  1411.         mv.visitVarInsn Opcodes.ASTORE, compiler.regs.thisObj
  1412.  
  1413.         # Initialize registers.
  1414.         for name in asm.unenclosedVars
  1415.             if (reg = asm.getRegister(name)) != -1
  1416.                 mv.visitInsn Opcodes.ACONST_NULL
  1417.                 mv.visitVarInsn Opcodes.ASTORE, reg
  1418.  
  1419.         # Hoist functions
  1420.         for stat in @ctx.node.stats when stat.type == 'defn-stat'
  1421.             asm.compileNode mv, stat
  1422.         # Compile code
  1423.         for stat in @ctx.node.stats when stat.type != 'defn-stat'
  1424.             asm.compileNode mv, stat
  1425.  
  1426.         # Return "exports" object.
  1427.         mv.visitVarInsn Opcodes.ALOAD, compiler.regs.thisObj
  1428.         mv.visitInsn Opcodes.ARETURN
  1429.  
  1430.         mv.visitMaxs 0, 0
  1431.         mv.visitEnd
  1432.         return
  1433.  
  1434. class ClosureContextCompiler extends ContextCompiler
  1435.  
  1436.     compileClass: ->
  1437.         cw = new ClassWriter(ClassWriter.COMPUTE_MAXS)
  1438.         qn = compiler.qn.context(@ctx.id)
  1439.         cw.visit(Opcodes.V1_6, Opcodes.ACC_SUPER + Opcodes.ACC_PUBLIC, qn, null, mug.qn.function, null)
  1440.         cw.visitSource @path, null
  1441.  
  1442.         @compileInit cw
  1443.         @compileMethods cw
  1444.         @compileFields cw
  1445.  
  1446.         # finalize
  1447.         cw.visitEnd()
  1448.         log.verbose '...Compiled closure ' + qn
  1449.         return [qn + '.class', cw.toByteArray()]
  1450.  
  1451.     compileInit: (cw) ->
  1452.         # Signature includes JSEnvironment and parent scopes.
  1453.         sig = jvm.sig.call jvm.sig.obj(mug.qn.toplevel),
  1454.             (jvm.sig.obj(compiler.qn.scope(parent.id)) for parent in @ctx.getParents())...,
  1455.             jvm.sig.void
  1456.  
  1457.         mv = cw.visitMethod Opcodes.ACC_PUBLIC, "<init>", sig, null, null
  1458.         qn = compiler.qn.context(@ctx.id)
  1459.         mv.visitCode()
  1460.         mv.visitVarInsn Opcodes.ALOAD, 0
  1461.         mv.visitVarInsn Opcodes.ALOAD, 1
  1462.         mv.visitMethodInsn Opcodes.INVOKESPECIAL, mug.qn.function, "<init>", jvm.sig.call(jvm.sig.obj(mug.qn.toplevel), jvm.sig.void)
  1463.  
  1464.         # Assign ENV property.
  1465.         mv.visitVarInsn Opcodes.ALOAD, 0
  1466.         mv.visitVarInsn Opcodes.ALOAD, 1
  1467.         mv.visitFieldInsn Opcodes.PUTFIELD, qn, compiler.ident.env, jvm.sig.obj(mug.qn.toplevel)
  1468.         # Assign SCOPE properties.
  1469.         for parent, i in @ctx.getParents()
  1470.             mv.visitVarInsn Opcodes.ALOAD, 0
  1471.             mv.visitVarInsn Opcodes.ALOAD, i + 2
  1472.             mv.visitFieldInsn Opcodes.PUTFIELD, qn, compiler.ident.scope(parent.id), jvm.sig.obj(compiler.qn.scope(parent.id))
  1473.  
  1474.         mv.visitInsn Opcodes.RETURN
  1475.         mv.visitMaxs 1, 1
  1476.         mv.visitEnd
  1477.         return
  1478.  
  1479.     compileFields: (cw) ->
  1480.         # Scope fields.
  1481.         cw.visitField(0, compiler.ident.env, jvm.sig.obj(mug.qn.toplevel), null, null).visitEnd()
  1482.         for parent in @ctx.getParents()
  1483.             cw.visitField(0, compiler.ident.scope(parent.id), jvm.sig.obj(compiler.qn.scope(parent.id)), null, null).visitEnd()
  1484.         return
  1485.  
  1486.     compileMethods: (cw) ->
  1487.         mv = cw.visitMethod Opcodes.ACC_PUBLIC, "invoke", compiler.sig.invoke(), null, compiler.stringArray(jvm.qn.exception)
  1488.         mv.visitCode()
  1489.  
  1490.         # start method compiler
  1491.         asm = new MethodCompiler @ctx
  1492.  
  1493.         # Create scope object.
  1494.         mv.visitTypeInsn Opcodes.NEW, compiler.qn.scope(@ctx.id)
  1495.         mv.visitInsn Opcodes.DUP
  1496.         mv.visitMethodInsn Opcodes.INVOKESPECIAL, compiler.qn.scope(@ctx.id), "<init>", jvm.sig.call(jvm.sig.void)
  1497.         mv.visitVarInsn Opcodes.ASTORE, compiler.regs.scope
  1498.  
  1499.         # Initialize arguments object.
  1500.         if @ctx.usesArguments
  1501.             asm.compileLoadEnvironment mv
  1502.             mv.visitVarInsn Opcodes.ILOAD, compiler.regs.argc
  1503.             for i in [compiler.regs.offset...compiler.regs.scope]
  1504.                 mv.visitVarInsn Opcodes.ALOAD, i
  1505.             mv.visitMethodInsn Opcodes.INVOKESTATIC, mug.qn.utils, "arguments", jvm.sig.call(
  1506.                 jvm.sig.integer,
  1507.                 jvm.sig.obj(jvm.qn.object), jvm.sig.obj(jvm.qn.object),
  1508.                 jvm.sig.obj(jvm.qn.object), jvm.sig.obj(jvm.qn.object),
  1509.                 jvm.sig.obj(jvm.qn.object), jvm.sig.obj(jvm.qn.object),
  1510.                 jvm.sig.obj(jvm.qn.object), jvm.sig.obj(jvm.qn.object),
  1511.                 jvm.sig.array(jvm.sig.obj(jvm.qn.object)),
  1512.                 jvm.sig.array(jvm.sig.obj(jvm.qn.object))
  1513.                 )
  1514.             mv.visitMethodInsn Opcodes.INVOKESTATIC, mug.qn.utils, "createArgumentsObject",
  1515.                 jvm.sig.call(jvm.sig.obj(mug.qn.toplevel), jvm.sig.array(jvm.sig.obj(jvm.qn.object)), jvm.sig.obj(mug.qn.object))
  1516.             if (reg = asm.getRegister("arguments")) != -1
  1517.                 mv.visitVarInsn Opcodes.ASTORE, reg
  1518.             else
  1519.                 mv.visitVarInsn Opcodes.ALOAD, compiler.regs.scope
  1520.                 mv.visitInsn Opcodes.SWAP
  1521.                 mv.visitMethodInsn Opcodes.INVOKEVIRTUAL, compiler.qn.scope(@ctx.id), "set_arguments", jvm.sig.call(jvm.sig.obj(jvm.qn.object), jvm.sig.void)
  1522.  
  1523.         # Initialize scoped arguments.
  1524.         for name, i in @ctx.node.args when asm.getRegister(name) == -1
  1525.             mv.visitVarInsn Opcodes.ALOAD, compiler.regs.scope
  1526.             mv.visitVarInsn Opcodes.ALOAD, i + 3
  1527.             mv.visitMethodInsn Opcodes.INVOKEVIRTUAL, compiler.qn.scope(@ctx.id), "set_" + name, jvm.sig.call(jvm.sig.obj(jvm.qn.object), jvm.sig.void)
  1528.         # Initialize registers.
  1529.         for name in asm.unenclosedVars when name not in @ctx.node.args and name not in [@ctx.node.name, 'arguments']
  1530.             mv.visitInsn Opcodes.ACONST_NULL
  1531.             mv.visitVarInsn Opcodes.ASTORE, asm.getRegister(name)
  1532.        
  1533.         # Initialize self.
  1534.         if @ctx.node.name and asm.getRegister(@ctx.node.name) == -1
  1535.             mv.visitVarInsn Opcodes.ALOAD, compiler.regs.scope
  1536.             mv.visitVarInsn Opcodes.ALOAD, 0
  1537.             mv.visitMethodInsn Opcodes.INVOKEVIRTUAL, compiler.qn.scope(@ctx.id), "set_" + @ctx.node.name, jvm.sig.call(jvm.sig.obj(jvm.qn.object), jvm.sig.void)
  1538.        
  1539.         # Hoist functions
  1540.         for stat in @ctx.node.stats when stat.type == 'defn-stat'
  1541.             asm.compileNode mv, stat
  1542.         # Compile code
  1543.         for stat in @ctx.node.stats when stat.type != 'defn-stat'
  1544.             asm.compileNode mv, stat
  1545.            
  1546.         # Catch-all return
  1547.         mv.visitInsn Opcodes.ACONST_NULL
  1548.         mv.visitInsn Opcodes.ARETURN
  1549.  
  1550.         # Finish closure.
  1551.         mv.visitMaxs 0, 0
  1552.         mv.visitEnd()
  1553.         return
  1554.  
  1555. #######################################################################
  1556. # AST analysis
  1557. #######################################################################
  1558.  
  1559. class Context
  1560.     constructor: (@node) ->
  1561.         @usesArguments = if @node.type == 'script-context' then false
  1562.         else jast.usesArguments(@node)
  1563.         @childNodes = jast.childContexts(@node)
  1564.         @localUndefinedRefs = jast.localUndefinedRefs(@node)
  1565.         @localVars = jast.localVars(@node)
  1566.         @children = []
  1567.    
  1568.     pushChild: (ctx) ->
  1569.         @children.push ctx
  1570.         ctx.parent = this
  1571.         return
  1572.    
  1573.     getLineage: ->
  1574.         line = []; cur = this
  1575.         while cur
  1576.             line.push(cur)
  1577.             cur = cur.parent
  1578.         line.reverse()
  1579.         return line
  1580.  
  1581.     getParents: -> @getLineage().slice(0, -1)
  1582.  
  1583.     postAnalyze: ->
  1584.         # get enclosed vars of child contexts
  1585.         getEnclosedVars = (ctx, vars) ->
  1586.             ret = []
  1587.             for child in ctx.children
  1588.                 ret.push(ref for ref in child.localUndefinedRefs when ref in vars)
  1589.                 ret.push getEnclosedVars(child, v for v in vars when v not in child.localVars)
  1590.             return [].concat(ret...)
  1591.         # set arrays
  1592.         @enclosedVars = getEnclosedVars(this, @localVars)
  1593.         @unenclosedVars = (v for v in @localVars when v not in @enclosedVars)
  1594.  
  1595.         # postanalyze children
  1596.         child.postAnalyze() for child in @children
  1597.         return
  1598.  
  1599. class CodeInput
  1600.     constructor: (@code) ->
  1601.         # generate AST
  1602.         @ast = jast.parse code
  1603.  
  1604.         # code analysis
  1605.         @numbers = jast.numbers(@ast)
  1606.         @regexps = jast.regexps(@ast)
  1607.  
  1608.         @contexts = []
  1609.         @rootContext = @buildContextTree()
  1610.         @rootContext.postAnalyze()
  1611.    
  1612.     buildContextTree: (node = @ast, parent = null) ->
  1613.         ctx = new Context(node, parent)
  1614.         ctx.id = @contexts.push(ctx)-1
  1615.         for child in ctx.childNodes
  1616.             ctx.pushChild @buildContextTree(child, node)
  1617.         return ctx
  1618.    
  1619.     getContext: (node) ->
  1620.         for ctx in @contexts
  1621.             return ctx if ctx.node == node
  1622.         throw new Error 'Unmatched context.'
  1623.  
  1624. #######################################################################
  1625. # Entry point
  1626. #######################################################################
  1627.  
  1628. compiler.readFile = (filepath) ->
  1629.     {Scanner} = java.util; {File} = java.io
  1630.  
  1631.     try
  1632.         return new Scanner(new File(filepath)).useDelimiter("\\Z").next()
  1633.     catch e
  1634.         return ""
  1635.  
  1636. compiler.writeClasses = (outpath, files) ->
  1637.     {File, FileOutputStream} = java.io
  1638.  
  1639.     for [path, bytes] in files
  1640.         try
  1641.             file = new File(outpath + path)
  1642.             file.getParentFile().mkdirs()
  1643.             stream = new FileOutputStream(file)
  1644.             stream.write(bytes)
  1645.         catch e
  1646.             console.log('Error writing out file:', e)
  1647.         finally
  1648.             stream.close()
  1649.  
  1650. compiler.writeJar = (outpath, jarpath, files) ->
  1651.     {File, FileOutputStream} = java.io
  1652.     {Manifest, Attributes, JarOutputStream, JarEntry} = java.util.jar
  1653.  
  1654.     manifest = new Manifest()
  1655.     attributes = manifest.getMainAttributes()
  1656.     #[TODO] write out manifest attributes?
  1657.     attributes.put(Attributes.Name.MANIFEST_VERSION, "1.0")
  1658.  
  1659.     new File(outpath).mkdirs()
  1660.     fstream = new FileOutputStream(outpath + jarpath)
  1661.     stream = new JarOutputStream(fstream, manifest)
  1662.  
  1663.     for [path, bytes] in files
  1664.         entry = new JarEntry(path)
  1665.         stream.putNextEntry(entry)
  1666.         stream.write(bytes)
  1667.  
  1668.     stream.flush()
  1669.     stream.close()
  1670.  
  1671. # start parsing
  1672.  
  1673. input = null
  1674.  
  1675. compiler.cli = (args) ->
  1676.     if args.length == 0
  1677.         throw new Error('Please specify a filename.')
  1678.  
  1679.     opts =
  1680.         output: "out/"
  1681.         jar: null
  1682.         files: {"": []}
  1683.  
  1684.     # arguments parsing loop
  1685.     curns = ""
  1686.     while args.length
  1687.         switch args[0]
  1688.             when '-h', '--help'
  1689.                 console.log """Usage:
  1690.  --output, -o <arg>    Output directory (default "out")
  1691.  --jar, -j <arg>       Create jar archive in output directory
  1692.  --namespace, -n <arg>  Namespace to use for subsequent modules (default "")"""
  1693.                 return
  1694.             when '-o', '--output'
  1695.                 args.shift()
  1696.                 opts.output = args.shift().replace(/\/?$/, '/')
  1697.             when '-j', '--jar'
  1698.                 args.shift()
  1699.                 opts.jar = args.shift()
  1700.             when '-n', '--namespace'
  1701.                 args.shift()
  1702.                 curns = args.shift().replace(/\./g, '/') + '/'
  1703.                 opts.files[curns] or= []
  1704.             else
  1705.                 opts.files[curns].push args.shift()
  1706.  
  1707.     log.notify 'Starting compilation.'
  1708.  
  1709.     # output files
  1710.     files = []
  1711.  
  1712.     for ns of opts.files
  1713.         for filepath in opts.files[ns]
  1714.             # Script name and qualified name.
  1715.             scriptname = filepath.replace(/\.js$/,'').replace(/^.*\//, '').replace(/\-/g, '_')
  1716.             compiler.qn.script = 'js/' + ns + scriptname
  1717.  
  1718.             log.notify "Compiling file #{filepath} as '#{compiler.qn.script}'..."
  1719.  
  1720.  
  1721.             # Parse cde.
  1722.             code = compiler.readFile(filepath) or ""
  1723.             input = new CodeInput(code)
  1724.             log.verbose 'Finished parsing.'
  1725.  
  1726.             # Constants class.
  1727.             files.push (new ConstantsCompiler).compileClass()
  1728.             # Context classes.
  1729.             for ctx in input.contexts
  1730.                 log.verbose 'Compiling context #' + ctx.id
  1731.                 if ctx.node.type == 'closure-context'
  1732.                     cmp = new ClosureContextCompiler filepath, ctx
  1733.                     files.push cmp.compileScopeClass()
  1734.                     files.push cmp.compileClass()
  1735.                 else if ctx.node.type == 'script-context'
  1736.                     cmp = new ScriptContextCompiler filepath, ctx
  1737.                     files.push cmp.compileScopeClass()
  1738.                     files.push cmp.compileClass()
  1739.  
  1740.     # write out classes
  1741.     if opts.jar != null
  1742.         log.notify "Writing out classes to archive '#{opts.output}#{opts.jar}'..."
  1743.         compiler.writeJar(opts.output, opts.jar, files)
  1744.     else
  1745.         log.notify "Writing out classes to folder '#{opts.output}'..."
  1746.         compiler.writeClasses(opts.output, files)
  1747.  
  1748.     # Done.
  1749.     end = new Date()
  1750.     log.notify 'Compiled in', (end-start)/1000, 's'
  1751.  
  1752. # launch
  1753. compiler.cli([].slice.call(arguments))
Add Comment
Please, Sign In to add comment