mirror of
https://github.com/darlinghq/darling-JavaScriptCore.git
synced 2024-11-23 04:09:40 +00:00
1511 lines
27 KiB
Ruby
1511 lines
27 KiB
Ruby
# Copyright (C) 2011-2020 Apple Inc. All rights reserved.
|
|
#
|
|
# Redistribution and use in source and binary forms, with or without
|
|
# modification, are permitted provided that the following conditions
|
|
# are met:
|
|
# 1. Redistributions of source code must retain the above copyright
|
|
# notice, this list of conditions and the following disclaimer.
|
|
# 2. Redistributions in binary form must reproduce the above copyright
|
|
# notice, this list of conditions and the following disclaimer in the
|
|
# documentation and/or other materials provided with the distribution.
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
|
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
|
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
|
# THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
require "config"
|
|
|
|
#
|
|
# Base utility types for the AST.
|
|
#
|
|
|
|
# Valid methods for Node:
|
|
#
|
|
# node.children -> Returns an array of immediate children.
|
|
#
|
|
# node.descendants -> Returns an array of all strict descendants (children
|
|
# and children of children, transitively).
|
|
#
|
|
# node.flatten -> Returns an array containing the strict descendants and
|
|
# the node itself.
|
|
#
|
|
# node.filter(type) -> Returns an array containing those elements in
|
|
# node.flatten that are of the given type (is_a? type returns true).
|
|
#
|
|
# node.mapChildren{|v| ...} -> Returns a new node with all children
|
|
# replaced according to the given block.
|
|
#
|
|
# Examples:
|
|
#
|
|
# node.filter(Setting).uniq -> Returns all of the settings that the AST's
|
|
# IfThenElse blocks depend on.
|
|
#
|
|
# node.filter(StructOffset).uniq -> Returns all of the structure offsets
|
|
# that the AST depends on.
|
|
|
|
class Node
|
|
attr_reader :codeOrigin
|
|
|
|
def initialize(codeOrigin)
|
|
@codeOrigin = codeOrigin
|
|
end
|
|
|
|
def codeOriginString
|
|
@codeOrigin.to_s
|
|
end
|
|
|
|
def descendants
|
|
children.collect{|v| v.flatten}.flatten
|
|
end
|
|
|
|
def flatten
|
|
[self] + descendants
|
|
end
|
|
|
|
def filter(type)
|
|
flatten.select{|v| v.is_a? type}
|
|
end
|
|
end
|
|
|
|
class NoChildren < Node
|
|
def initialize(codeOrigin)
|
|
super(codeOrigin)
|
|
end
|
|
|
|
def children
|
|
[]
|
|
end
|
|
|
|
def mapChildren
|
|
self
|
|
end
|
|
end
|
|
|
|
class StructOffsetKey
|
|
attr_reader :struct, :field
|
|
|
|
def initialize(struct, field)
|
|
@struct = struct
|
|
@field = field
|
|
end
|
|
|
|
def hash
|
|
@struct.hash + @field.hash * 3
|
|
end
|
|
|
|
def eql?(other)
|
|
@struct == other.struct and @field == other.field
|
|
end
|
|
end
|
|
|
|
#
|
|
# AST nodes.
|
|
#
|
|
|
|
class StructOffset < NoChildren
|
|
attr_reader :struct, :field
|
|
|
|
def initialize(codeOrigin, struct, field)
|
|
super(codeOrigin)
|
|
@struct = struct
|
|
@field = field
|
|
end
|
|
|
|
@@mapping = {}
|
|
|
|
def self.forField(codeOrigin, struct, field)
|
|
key = StructOffsetKey.new(struct, field)
|
|
|
|
unless @@mapping[key]
|
|
@@mapping[key] = StructOffset.new(codeOrigin, struct, field)
|
|
end
|
|
@@mapping[key]
|
|
end
|
|
|
|
def dump
|
|
"#{struct}::#{field}"
|
|
end
|
|
|
|
def <=>(other)
|
|
if @struct != other.struct
|
|
return @struct <=> other.struct
|
|
end
|
|
@field <=> other.field
|
|
end
|
|
|
|
def address?
|
|
false
|
|
end
|
|
|
|
def label?
|
|
false
|
|
end
|
|
|
|
def immediate?
|
|
true
|
|
end
|
|
|
|
def register?
|
|
false
|
|
end
|
|
end
|
|
|
|
class Sizeof < NoChildren
|
|
attr_reader :struct
|
|
|
|
def initialize(codeOrigin, struct)
|
|
super(codeOrigin)
|
|
@struct = struct
|
|
end
|
|
|
|
@@mapping = {}
|
|
|
|
def self.forName(codeOrigin, struct)
|
|
unless @@mapping[struct]
|
|
@@mapping[struct] = Sizeof.new(codeOrigin, struct)
|
|
end
|
|
@@mapping[struct]
|
|
end
|
|
|
|
def dump
|
|
"sizeof #{@struct}"
|
|
end
|
|
|
|
def <=>(other)
|
|
@struct <=> other.struct
|
|
end
|
|
|
|
def address?
|
|
false
|
|
end
|
|
|
|
def label?
|
|
false
|
|
end
|
|
|
|
def immediate?
|
|
true
|
|
end
|
|
|
|
def register?
|
|
false
|
|
end
|
|
end
|
|
|
|
class Immediate < NoChildren
|
|
attr_reader :value
|
|
|
|
def initialize(codeOrigin, value)
|
|
super(codeOrigin)
|
|
@value = value
|
|
raise "Bad immediate value #{value.inspect} at #{codeOriginString}" unless value.is_a? Integer
|
|
end
|
|
|
|
def dump
|
|
"#{value}"
|
|
end
|
|
|
|
def ==(other)
|
|
other.is_a? Immediate and other.value == @value
|
|
end
|
|
|
|
def address?
|
|
false
|
|
end
|
|
|
|
def label?
|
|
false
|
|
end
|
|
|
|
def immediate?
|
|
true
|
|
end
|
|
|
|
def immediateOperand?
|
|
true
|
|
end
|
|
|
|
def register?
|
|
false
|
|
end
|
|
end
|
|
|
|
class AddImmediates < Node
|
|
attr_reader :left, :right
|
|
|
|
def initialize(codeOrigin, left, right)
|
|
super(codeOrigin)
|
|
@left = left
|
|
@right = right
|
|
end
|
|
|
|
def children
|
|
[@left, @right]
|
|
end
|
|
|
|
def mapChildren
|
|
AddImmediates.new(codeOrigin, (yield @left), (yield @right))
|
|
end
|
|
|
|
def dump
|
|
"(#{left.dump} + #{right.dump})"
|
|
end
|
|
|
|
def value
|
|
"#{left.value} + #{right.value}"
|
|
end
|
|
|
|
def address?
|
|
false
|
|
end
|
|
|
|
def label?
|
|
false
|
|
end
|
|
|
|
def immediate?
|
|
true
|
|
end
|
|
|
|
def immediateOperand?
|
|
true
|
|
end
|
|
|
|
def register?
|
|
false
|
|
end
|
|
end
|
|
|
|
class SubImmediates < Node
|
|
attr_reader :left, :right
|
|
|
|
def initialize(codeOrigin, left, right)
|
|
super(codeOrigin)
|
|
@left = left
|
|
@right = right
|
|
end
|
|
|
|
def children
|
|
[@left, @right]
|
|
end
|
|
|
|
def mapChildren
|
|
SubImmediates.new(codeOrigin, (yield @left), (yield @right))
|
|
end
|
|
|
|
def dump
|
|
"(#{left.dump} - #{right.dump})"
|
|
end
|
|
|
|
def value
|
|
"#{left.value} - #{right.value}"
|
|
end
|
|
|
|
def address?
|
|
false
|
|
end
|
|
|
|
def label?
|
|
false
|
|
end
|
|
|
|
def immediate?
|
|
true
|
|
end
|
|
|
|
def immediateOperand?
|
|
true
|
|
end
|
|
|
|
def register?
|
|
false
|
|
end
|
|
end
|
|
|
|
class MulImmediates < Node
|
|
attr_reader :left, :right
|
|
|
|
def initialize(codeOrigin, left, right)
|
|
super(codeOrigin)
|
|
@left = left
|
|
@right = right
|
|
end
|
|
|
|
def children
|
|
[@left, @right]
|
|
end
|
|
|
|
def mapChildren
|
|
MulImmediates.new(codeOrigin, (yield @left), (yield @right))
|
|
end
|
|
|
|
def dump
|
|
"(#{left.dump} * #{right.dump})"
|
|
end
|
|
|
|
def address?
|
|
false
|
|
end
|
|
|
|
def label?
|
|
false
|
|
end
|
|
|
|
def immediate?
|
|
true
|
|
end
|
|
|
|
def immediateOperand?
|
|
false
|
|
end
|
|
|
|
def register?
|
|
false
|
|
end
|
|
end
|
|
|
|
class NegImmediate < Node
|
|
attr_reader :child
|
|
|
|
def initialize(codeOrigin, child)
|
|
super(codeOrigin)
|
|
@child = child
|
|
end
|
|
|
|
def children
|
|
[@child]
|
|
end
|
|
|
|
def mapChildren
|
|
NegImmediate.new(codeOrigin, (yield @child))
|
|
end
|
|
|
|
def dump
|
|
"(-#{@child.dump})"
|
|
end
|
|
|
|
def address?
|
|
false
|
|
end
|
|
|
|
def label?
|
|
false
|
|
end
|
|
|
|
def immediate?
|
|
true
|
|
end
|
|
|
|
def immediateOperand?
|
|
false
|
|
end
|
|
|
|
def register?
|
|
false
|
|
end
|
|
end
|
|
|
|
class OrImmediates < Node
|
|
attr_reader :left, :right
|
|
|
|
def initialize(codeOrigin, left, right)
|
|
super(codeOrigin)
|
|
@left = left
|
|
@right = right
|
|
end
|
|
|
|
def children
|
|
[@left, @right]
|
|
end
|
|
|
|
def mapChildren
|
|
OrImmediates.new(codeOrigin, (yield @left), (yield @right))
|
|
end
|
|
|
|
def dump
|
|
"(#{left.dump} | #{right.dump})"
|
|
end
|
|
|
|
def address?
|
|
false
|
|
end
|
|
|
|
def label?
|
|
false
|
|
end
|
|
|
|
def immediate?
|
|
true
|
|
end
|
|
|
|
def immediateOperand?
|
|
false
|
|
end
|
|
|
|
def register?
|
|
false
|
|
end
|
|
end
|
|
|
|
class AndImmediates < Node
|
|
attr_reader :left, :right
|
|
|
|
def initialize(codeOrigin, left, right)
|
|
super(codeOrigin)
|
|
@left = left
|
|
@right = right
|
|
end
|
|
|
|
def children
|
|
[@left, @right]
|
|
end
|
|
|
|
def mapChildren
|
|
AndImmediates.new(codeOrigin, (yield @left), (yield @right))
|
|
end
|
|
|
|
def dump
|
|
"(#{left.dump} & #{right.dump})"
|
|
end
|
|
|
|
def address?
|
|
false
|
|
end
|
|
|
|
def label?
|
|
false
|
|
end
|
|
|
|
def immediate?
|
|
true
|
|
end
|
|
|
|
def immediateOperand?
|
|
false
|
|
end
|
|
|
|
def register?
|
|
false
|
|
end
|
|
end
|
|
|
|
class XorImmediates < Node
|
|
attr_reader :left, :right
|
|
|
|
def initialize(codeOrigin, left, right)
|
|
super(codeOrigin)
|
|
@left = left
|
|
@right = right
|
|
end
|
|
|
|
def children
|
|
[@left, @right]
|
|
end
|
|
|
|
def mapChildren
|
|
XorImmediates.new(codeOrigin, (yield @left), (yield @right))
|
|
end
|
|
|
|
def dump
|
|
"(#{left.dump} ^ #{right.dump})"
|
|
end
|
|
|
|
def address?
|
|
false
|
|
end
|
|
|
|
def label?
|
|
false
|
|
end
|
|
|
|
def immediate?
|
|
true
|
|
end
|
|
|
|
def immediateOperand?
|
|
false
|
|
end
|
|
|
|
def register?
|
|
false
|
|
end
|
|
end
|
|
|
|
class BitnotImmediate < Node
|
|
attr_reader :child
|
|
|
|
def initialize(codeOrigin, child)
|
|
super(codeOrigin)
|
|
@child = child
|
|
end
|
|
|
|
def children
|
|
[@child]
|
|
end
|
|
|
|
def mapChildren
|
|
BitnotImmediate.new(codeOrigin, (yield @child))
|
|
end
|
|
|
|
def dump
|
|
"(~#{@child.dump})"
|
|
end
|
|
|
|
def address?
|
|
false
|
|
end
|
|
|
|
def label?
|
|
false
|
|
end
|
|
|
|
def immediate?
|
|
true
|
|
end
|
|
|
|
def immediateOperand?
|
|
false
|
|
end
|
|
|
|
def register?
|
|
false
|
|
end
|
|
end
|
|
|
|
class StringLiteral < NoChildren
|
|
attr_reader :value
|
|
|
|
def initialize(codeOrigin, value)
|
|
super(codeOrigin)
|
|
@value = value[1..-2]
|
|
raise "Bad string literal #{value.inspect} at #{codeOriginString}" unless value.is_a? String
|
|
end
|
|
|
|
def dump
|
|
"#{value}"
|
|
end
|
|
|
|
def ==(other)
|
|
other.is_a? StringLiteral and other.value == @value
|
|
end
|
|
|
|
def address?
|
|
false
|
|
end
|
|
|
|
def label?
|
|
false
|
|
end
|
|
|
|
def immediate?
|
|
false
|
|
end
|
|
|
|
def immediateOperand?
|
|
false
|
|
end
|
|
|
|
def register?
|
|
false
|
|
end
|
|
end
|
|
|
|
class RegisterID < NoChildren
|
|
attr_reader :name
|
|
|
|
def initialize(codeOrigin, name)
|
|
super(codeOrigin)
|
|
@name = name
|
|
end
|
|
|
|
@@mapping = {}
|
|
|
|
def self.forName(codeOrigin, name)
|
|
unless @@mapping[name]
|
|
@@mapping[name] = RegisterID.new(codeOrigin, name)
|
|
end
|
|
@@mapping[name]
|
|
end
|
|
|
|
def dump
|
|
name
|
|
end
|
|
|
|
def address?
|
|
false
|
|
end
|
|
|
|
def label?
|
|
false
|
|
end
|
|
|
|
def immediate?
|
|
false
|
|
end
|
|
|
|
def register?
|
|
true
|
|
end
|
|
end
|
|
|
|
class FPRegisterID < NoChildren
|
|
attr_reader :name
|
|
|
|
def initialize(codeOrigin, name)
|
|
super(codeOrigin)
|
|
@name = name
|
|
end
|
|
|
|
@@mapping = {}
|
|
|
|
def self.forName(codeOrigin, name)
|
|
unless @@mapping[name]
|
|
@@mapping[name] = FPRegisterID.new(codeOrigin, name)
|
|
end
|
|
@@mapping[name]
|
|
end
|
|
|
|
def dump
|
|
name
|
|
end
|
|
|
|
def address?
|
|
false
|
|
end
|
|
|
|
def label?
|
|
false
|
|
end
|
|
|
|
def immediate?
|
|
false
|
|
end
|
|
|
|
def immediateOperand?
|
|
false
|
|
end
|
|
|
|
def register?
|
|
true
|
|
end
|
|
end
|
|
|
|
class SpecialRegister < NoChildren
|
|
attr_reader :name
|
|
|
|
def initialize(name)
|
|
super(codeOrigin)
|
|
@name = name
|
|
end
|
|
|
|
def address?
|
|
false
|
|
end
|
|
|
|
def label?
|
|
false
|
|
end
|
|
|
|
def immediate?
|
|
false
|
|
end
|
|
|
|
def immediateOperand?
|
|
false
|
|
end
|
|
|
|
def register?
|
|
true
|
|
end
|
|
end
|
|
|
|
class Variable < NoChildren
|
|
attr_reader :name
|
|
|
|
def initialize(codeOrigin, name, originalName = nil)
|
|
super(codeOrigin)
|
|
@name = name
|
|
@originalName = originalName
|
|
end
|
|
|
|
@@mapping = {}
|
|
|
|
def self.forName(codeOrigin, name, originalName = nil)
|
|
unless @@mapping[name]
|
|
@@mapping[name] = Variable.new(codeOrigin, name, originalName)
|
|
end
|
|
@@mapping[name]
|
|
end
|
|
|
|
def originalName
|
|
@originalName || name
|
|
end
|
|
|
|
def dump
|
|
originalName
|
|
end
|
|
|
|
def inspect
|
|
"<variable #{originalName} at #{codeOriginString}>"
|
|
end
|
|
end
|
|
|
|
class Address < Node
|
|
attr_reader :base, :offset
|
|
|
|
def initialize(codeOrigin, base, offset)
|
|
super(codeOrigin)
|
|
@base = base
|
|
@offset = offset
|
|
raise "Bad base for address #{base.inspect} at #{codeOriginString}" unless base.is_a? Variable or base.register?
|
|
raise "Bad offset for address #{offset.inspect} at #{codeOriginString}" unless offset.is_a? Variable or offset.immediate?
|
|
end
|
|
|
|
def withOffset(extraOffset)
|
|
Address.new(codeOrigin, @base, Immediate.new(codeOrigin, @offset.value + extraOffset))
|
|
end
|
|
|
|
def children
|
|
[@base, @offset]
|
|
end
|
|
|
|
def mapChildren
|
|
Address.new(codeOrigin, (yield @base), (yield @offset))
|
|
end
|
|
|
|
def dump
|
|
"#{offset.dump}[#{base.dump}]"
|
|
end
|
|
|
|
def address?
|
|
true
|
|
end
|
|
|
|
def label?
|
|
false
|
|
end
|
|
|
|
def immediate?
|
|
false
|
|
end
|
|
|
|
def immediateOperand?
|
|
true
|
|
end
|
|
|
|
def register?
|
|
false
|
|
end
|
|
end
|
|
|
|
class BaseIndex < Node
|
|
attr_reader :base, :index, :scale, :offset
|
|
|
|
def initialize(codeOrigin, base, index, scale, offset)
|
|
super(codeOrigin)
|
|
@base = base
|
|
@index = index
|
|
@scale = scale
|
|
@offset = offset
|
|
end
|
|
|
|
def scaleValue
|
|
raise unless [1, 2, 4, 8].member? scale.value
|
|
scale.value
|
|
end
|
|
|
|
def scaleShift
|
|
case scaleValue
|
|
when 1
|
|
0
|
|
when 2
|
|
1
|
|
when 4
|
|
2
|
|
when 8
|
|
3
|
|
else
|
|
raise "Bad scale: #{scale.value} at #{codeOriginString}"
|
|
end
|
|
end
|
|
|
|
def withOffset(extraOffset)
|
|
BaseIndex.new(codeOrigin, @base, @index, @scale, Immediate.new(codeOrigin, @offset.value + extraOffset))
|
|
end
|
|
|
|
def children
|
|
[@base, @index, @offset]
|
|
end
|
|
|
|
def mapChildren
|
|
BaseIndex.new(codeOrigin, (yield @base), (yield @index), (yield @scale), (yield @offset))
|
|
end
|
|
|
|
def dump
|
|
"#{offset.dump}[#{base.dump}, #{index.dump}, #{scale.value}]"
|
|
end
|
|
|
|
def address?
|
|
true
|
|
end
|
|
|
|
def label?
|
|
false
|
|
end
|
|
|
|
def immediate?
|
|
false
|
|
end
|
|
|
|
def immediateOperand?
|
|
false
|
|
end
|
|
|
|
def register?
|
|
false
|
|
end
|
|
end
|
|
|
|
class AbsoluteAddress < NoChildren
|
|
attr_reader :address
|
|
|
|
def initialize(codeOrigin, address)
|
|
super(codeOrigin)
|
|
@address = address
|
|
end
|
|
|
|
def withOffset(extraOffset)
|
|
AbsoluteAddress.new(codeOrigin, Immediate.new(codeOrigin, @address.value + extraOffset))
|
|
end
|
|
|
|
def dump
|
|
"#{address.dump}[]"
|
|
end
|
|
|
|
def address?
|
|
true
|
|
end
|
|
|
|
def label?
|
|
false
|
|
end
|
|
|
|
def immediate?
|
|
false
|
|
end
|
|
|
|
def immediateOperand?
|
|
true
|
|
end
|
|
|
|
def register?
|
|
false
|
|
end
|
|
end
|
|
|
|
class Instruction < Node
|
|
attr_reader :opcode, :operands, :annotation
|
|
|
|
def initialize(codeOrigin, opcode, operands, annotation=nil)
|
|
super(codeOrigin)
|
|
@opcode = opcode
|
|
@operands = operands
|
|
@annotation = annotation
|
|
end
|
|
|
|
def cloneWithNewOperands(newOperands)
|
|
Instruction.new(self.codeOrigin, self.opcode, newOperands, self.annotation)
|
|
end
|
|
|
|
def children
|
|
operands
|
|
end
|
|
|
|
def mapChildren(&proc)
|
|
Instruction.new(codeOrigin, @opcode, @operands.map(&proc), @annotation)
|
|
end
|
|
|
|
def dump
|
|
"\t" + opcode.to_s + " " + operands.collect{|v| v.dump}.join(", ")
|
|
end
|
|
|
|
def lowerDefault
|
|
case opcode
|
|
when "localAnnotation"
|
|
$asm.putLocalAnnotation
|
|
when "globalAnnotation"
|
|
$asm.putGlobalAnnotation
|
|
when "emit"
|
|
$asm.puts "#{operands[0].dump}"
|
|
when "tagCodePtr", "tagReturnAddress", "untagReturnAddress", "removeCodePtrTag", "untagArrayPtr"
|
|
else
|
|
raise "Unhandled opcode #{opcode} at #{codeOriginString}"
|
|
end
|
|
end
|
|
|
|
def prepareToLower(backendName)
|
|
if respond_to?("recordMetaData#{backendName}")
|
|
send("recordMetaData#{backendName}")
|
|
else
|
|
recordMetaDataDefault
|
|
end
|
|
end
|
|
|
|
def recordMetaDataDefault
|
|
$asm.codeOrigin codeOriginString if $enableCodeOriginComments
|
|
$asm.annotation annotation if $enableInstrAnnotations
|
|
$asm.debugAnnotation codeOrigin.debugDirective if $enableDebugAnnotations
|
|
end
|
|
end
|
|
|
|
class Error < NoChildren
|
|
def initialize(codeOrigin)
|
|
super(codeOrigin)
|
|
end
|
|
|
|
def dump
|
|
"\terror"
|
|
end
|
|
end
|
|
|
|
class ConstExpr < NoChildren
|
|
attr_reader :value
|
|
|
|
def initialize(codeOrigin, value)
|
|
super(codeOrigin)
|
|
@value = value
|
|
end
|
|
|
|
@@mapping = {}
|
|
|
|
def self.forName(codeOrigin, text)
|
|
unless @@mapping[text]
|
|
@@mapping[text] = ConstExpr.new(codeOrigin, text)
|
|
end
|
|
@@mapping[text]
|
|
end
|
|
|
|
def dump
|
|
"constexpr (#{@value.dump})"
|
|
end
|
|
|
|
def <=>(other)
|
|
@value <=> other.value
|
|
end
|
|
|
|
def immediate?
|
|
true
|
|
end
|
|
end
|
|
|
|
class ConstDecl < Node
|
|
attr_reader :variable, :value
|
|
|
|
def initialize(codeOrigin, variable, value)
|
|
super(codeOrigin)
|
|
@variable = variable
|
|
@value = value
|
|
end
|
|
|
|
def children
|
|
[@variable, @value]
|
|
end
|
|
|
|
def mapChildren
|
|
ConstDecl.new(codeOrigin, (yield @variable), (yield @value))
|
|
end
|
|
|
|
def dump
|
|
"const #{@variable.dump} = #{@value.dump}"
|
|
end
|
|
end
|
|
|
|
$labelMapping = {}
|
|
$referencedExternLabels = Array.new
|
|
|
|
class Label < NoChildren
|
|
def initialize(codeOrigin, name, definedInFile = false)
|
|
super(codeOrigin)
|
|
@name = name
|
|
@definedInFile = definedInFile
|
|
@extern = true
|
|
@global = false
|
|
end
|
|
|
|
def self.forName(codeOrigin, name, definedInFile = false)
|
|
if $labelMapping[name]
|
|
raise "Label name collision: #{name}" unless $labelMapping[name].is_a? Label
|
|
else
|
|
$labelMapping[name] = Label.new(codeOrigin, name, definedInFile)
|
|
end
|
|
if definedInFile
|
|
$labelMapping[name].clearExtern()
|
|
end
|
|
$labelMapping[name]
|
|
end
|
|
|
|
def self.setAsGlobal(codeOrigin, name)
|
|
if $labelMapping[name]
|
|
label = $labelMapping[name]
|
|
raise "Label: #{name} declared global multiple times" unless not label.global?
|
|
label.setGlobal()
|
|
else
|
|
newLabel = Label.new(codeOrigin, name)
|
|
newLabel.setGlobal()
|
|
$labelMapping[name] = newLabel
|
|
end
|
|
end
|
|
|
|
def self.resetReferenced
|
|
$referencedExternLabels = Array.new
|
|
end
|
|
|
|
def self.forReferencedExtern()
|
|
$referencedExternLabels.each {
|
|
| label |
|
|
yield "#{label.name}"
|
|
}
|
|
end
|
|
|
|
def clearExtern
|
|
@extern = false
|
|
end
|
|
|
|
def extern?
|
|
@extern
|
|
end
|
|
|
|
def setGlobal
|
|
@global = true
|
|
end
|
|
|
|
def global?
|
|
@global
|
|
end
|
|
|
|
def name
|
|
@name
|
|
end
|
|
|
|
def dump
|
|
"#{name}:"
|
|
end
|
|
end
|
|
|
|
class LocalLabel < NoChildren
|
|
attr_reader :name
|
|
|
|
def initialize(codeOrigin, name)
|
|
super(codeOrigin)
|
|
@name = name
|
|
end
|
|
|
|
@@uniqueNameCounter = 0
|
|
|
|
def self.forName(codeOrigin, name)
|
|
if $labelMapping[name]
|
|
raise "Label name collision: #{name}" unless $labelMapping[name].is_a? LocalLabel
|
|
else
|
|
$labelMapping[name] = LocalLabel.new(codeOrigin, name)
|
|
end
|
|
$labelMapping[name]
|
|
end
|
|
|
|
def self.unique(comment)
|
|
newName = "_#{comment}"
|
|
if $emitWinAsm and newName.length > 90
|
|
newName = newName[0...45] + "___" + newName[-45..-1]
|
|
end
|
|
if $labelMapping[newName]
|
|
while $labelMapping[newName = "_#{@@uniqueNameCounter}_#{comment}"]
|
|
@@uniqueNameCounter += 1
|
|
end
|
|
end
|
|
forName(nil, newName)
|
|
end
|
|
|
|
def cleanName
|
|
if name =~ /^\./
|
|
"_" + name[1..-1]
|
|
else
|
|
name
|
|
end
|
|
end
|
|
|
|
def dump
|
|
"#{name}:"
|
|
end
|
|
end
|
|
|
|
class LabelReference < Node
|
|
attr_reader :label
|
|
attr_accessor :offset
|
|
|
|
def initialize(codeOrigin, label)
|
|
super(codeOrigin)
|
|
@label = label
|
|
@offset = 0
|
|
end
|
|
|
|
def plusOffset(additionalOffset)
|
|
result = LabelReference.new(codeOrigin, label)
|
|
result.offset = @offset + additionalOffset
|
|
result
|
|
end
|
|
|
|
def children
|
|
[@label]
|
|
end
|
|
|
|
def mapChildren
|
|
result = LabelReference.new(codeOrigin, (yield @label))
|
|
result.offset = @offset
|
|
result
|
|
end
|
|
|
|
def name
|
|
label.name
|
|
end
|
|
|
|
def extern?
|
|
$labelMapping[name].is_a? Label and $labelMapping[name].extern?
|
|
end
|
|
|
|
def used
|
|
if !$referencedExternLabels.include?(@label) and extern?
|
|
$referencedExternLabels.push(@label)
|
|
end
|
|
end
|
|
|
|
def dump
|
|
label.name
|
|
end
|
|
|
|
def value
|
|
asmLabel()
|
|
end
|
|
|
|
def address?
|
|
false
|
|
end
|
|
|
|
def label?
|
|
true
|
|
end
|
|
|
|
def immediate?
|
|
false
|
|
end
|
|
|
|
def immediateOperand?
|
|
true
|
|
end
|
|
end
|
|
|
|
class LocalLabelReference < NoChildren
|
|
attr_reader :label
|
|
|
|
def initialize(codeOrigin, label)
|
|
super(codeOrigin)
|
|
@label = label
|
|
end
|
|
|
|
def children
|
|
[@label]
|
|
end
|
|
|
|
def mapChildren
|
|
LocalLabelReference.new(codeOrigin, (yield @label))
|
|
end
|
|
|
|
def name
|
|
label.name
|
|
end
|
|
|
|
def dump
|
|
label.name
|
|
end
|
|
|
|
def value
|
|
asmLabel()
|
|
end
|
|
|
|
def address?
|
|
false
|
|
end
|
|
|
|
def label?
|
|
true
|
|
end
|
|
|
|
def immediate?
|
|
false
|
|
end
|
|
|
|
def immediateOperand?
|
|
true
|
|
end
|
|
end
|
|
|
|
class Sequence < Node
|
|
attr_reader :list
|
|
|
|
def initialize(codeOrigin, list)
|
|
super(codeOrigin)
|
|
@list = list
|
|
end
|
|
|
|
def children
|
|
list
|
|
end
|
|
|
|
def mapChildren(&proc)
|
|
Sequence.new(codeOrigin, @list.map(&proc))
|
|
end
|
|
|
|
def dump
|
|
list.collect{|v| v.dump}.join("\n")
|
|
end
|
|
end
|
|
|
|
class True < NoChildren
|
|
def initialize
|
|
super(nil)
|
|
end
|
|
|
|
@@instance = True.new
|
|
|
|
def self.instance
|
|
@@instance
|
|
end
|
|
|
|
def value
|
|
true
|
|
end
|
|
|
|
def dump
|
|
"true"
|
|
end
|
|
end
|
|
|
|
class False < NoChildren
|
|
def initialize
|
|
super(nil)
|
|
end
|
|
|
|
@@instance = False.new
|
|
|
|
def self.instance
|
|
@@instance
|
|
end
|
|
|
|
def value
|
|
false
|
|
end
|
|
|
|
def dump
|
|
"false"
|
|
end
|
|
end
|
|
|
|
class TrueClass
|
|
def asNode
|
|
True.instance
|
|
end
|
|
end
|
|
|
|
class FalseClass
|
|
def asNode
|
|
False.instance
|
|
end
|
|
end
|
|
|
|
class Setting < NoChildren
|
|
attr_reader :name
|
|
|
|
def initialize(codeOrigin, name)
|
|
super(codeOrigin)
|
|
@name = name
|
|
end
|
|
|
|
@@mapping = {}
|
|
|
|
def self.forName(codeOrigin, name)
|
|
unless @@mapping[name]
|
|
@@mapping[name] = Setting.new(codeOrigin, name)
|
|
end
|
|
@@mapping[name]
|
|
end
|
|
|
|
def dump
|
|
name
|
|
end
|
|
end
|
|
|
|
class And < Node
|
|
attr_reader :left, :right
|
|
|
|
def initialize(codeOrigin, left, right)
|
|
super(codeOrigin)
|
|
@left = left
|
|
@right = right
|
|
end
|
|
|
|
def children
|
|
[@left, @right]
|
|
end
|
|
|
|
def mapChildren
|
|
And.new(codeOrigin, (yield @left), (yield @right))
|
|
end
|
|
|
|
def dump
|
|
"(#{left.dump} and #{right.dump})"
|
|
end
|
|
end
|
|
|
|
class Or < Node
|
|
attr_reader :left, :right
|
|
|
|
def initialize(codeOrigin, left, right)
|
|
super(codeOrigin)
|
|
@left = left
|
|
@right = right
|
|
end
|
|
|
|
def children
|
|
[@left, @right]
|
|
end
|
|
|
|
def mapChildren
|
|
Or.new(codeOrigin, (yield @left), (yield @right))
|
|
end
|
|
|
|
def dump
|
|
"(#{left.dump} or #{right.dump})"
|
|
end
|
|
end
|
|
|
|
class Not < Node
|
|
attr_reader :child
|
|
|
|
def initialize(codeOrigin, child)
|
|
super(codeOrigin)
|
|
@child = child
|
|
end
|
|
|
|
def children
|
|
[@child]
|
|
end
|
|
|
|
def mapChildren
|
|
Not.new(codeOrigin, (yield @child))
|
|
end
|
|
|
|
def dump
|
|
"(not #{child.dump})"
|
|
end
|
|
end
|
|
|
|
class Skip < NoChildren
|
|
def initialize(codeOrigin)
|
|
super(codeOrigin)
|
|
end
|
|
|
|
def dump
|
|
"\tskip"
|
|
end
|
|
end
|
|
|
|
class IfThenElse < Node
|
|
attr_reader :predicate, :thenCase
|
|
attr_accessor :elseCase
|
|
|
|
def initialize(codeOrigin, predicate, thenCase)
|
|
super(codeOrigin)
|
|
@predicate = predicate
|
|
@thenCase = thenCase
|
|
@elseCase = Skip.new(codeOrigin)
|
|
end
|
|
|
|
def children
|
|
if @elseCase
|
|
[@predicate, @thenCase, @elseCase]
|
|
else
|
|
[@predicate, @thenCase]
|
|
end
|
|
end
|
|
|
|
def mapChildren
|
|
ifThenElse = IfThenElse.new(codeOrigin, (yield @predicate), (yield @thenCase))
|
|
ifThenElse.elseCase = yield @elseCase
|
|
ifThenElse
|
|
end
|
|
|
|
def dump
|
|
"if #{predicate.dump}\n" + thenCase.dump + "\nelse\n" + elseCase.dump + "\nend"
|
|
end
|
|
end
|
|
|
|
class Macro < Node
|
|
attr_reader :name, :variables, :body
|
|
|
|
def initialize(codeOrigin, name, variables, body)
|
|
super(codeOrigin)
|
|
@name = name
|
|
@variables = variables
|
|
@body = body
|
|
end
|
|
|
|
def children
|
|
@variables + [@body]
|
|
end
|
|
|
|
def mapChildren
|
|
Macro.new(codeOrigin, @name, @variables.map{|v| yield v}, (yield @body))
|
|
end
|
|
|
|
def dump
|
|
"macro #{name}(" + variables.collect{|v| v.dump}.join(", ") + ")\n" + body.dump + "\nend"
|
|
end
|
|
end
|
|
|
|
class MacroCall < Node
|
|
attr_reader :name, :operands, :annotation
|
|
|
|
def initialize(codeOrigin, name, operands, annotation, originalName = nil)
|
|
super(codeOrigin)
|
|
@name = name
|
|
@operands = operands
|
|
raise unless @operands
|
|
@operands.each{|v| raise unless v}
|
|
@annotation = annotation
|
|
@originalName = originalName
|
|
end
|
|
|
|
def originalName
|
|
@originalName || name
|
|
end
|
|
|
|
def children
|
|
@operands
|
|
end
|
|
|
|
def mapChildren(&proc)
|
|
MacroCall.new(codeOrigin, @name, @operands.map(&proc), @annotation, @originalName)
|
|
end
|
|
|
|
def dump
|
|
"\t#{originalName}(" + operands.collect{|v| v.dump}.join(", ") + ")"
|
|
end
|
|
end
|
|
|