/* * Copyright (C) 2016-2017 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. ``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 * 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. */ "use strict"; /* * Base utility types for the AST. * * Valid methods for Node: * * node.children -> Returns an array of immediate children. It has been modified * from the original Ruby version to dump directly nearly the original source. * * node.descendents -> Returns an array of all strict descendants (children * and children of children, transitively). * */ class Node { constructor(codeOrigin) { this._codeOrigin = codeOrigin; } get codeOriginString() { return this._codeOrigin.toString(); } get codeOrigin() { return this._codeOrigin; } } class NoChildren extends Node { constructor(codeOrigin) { super(codeOrigin); } children() { return []; } } function structOffsetKey(struct, field) { return struct + "::" + field; } // AST nodes. class StructOffset extends NoChildren { constructor(codeOrigin, struct, field) { super(codeOrigin); this._struct = struct; this._field = field; } static forField(codeOrigin, struct, field) { let key = structOffsetKey(struct, field); if (!this.mapping[key]) this.mapping[key] = new StructOffset(codeOrigin, struct, field); return this.mapping[key]; } static resetMappings() { this.mapping = {}; } dump() { return this.struct + "::" + this.field; } compare(other) { if (this.struct != other.struct) return this.struct.localeCompare(other.struct); return this.field.localeCompare(other.field); } get struct() { return this._struct; } get field() { return this._field; } get isAddress() { return false; } get isLabel() { return false; } get isImmediate() { return true; } get isRegister() { return false; } } StructOffset.mapping = {}; class Sizeof extends NoChildren { constructor(codeOrigin, struct) { super(codeOrigin); this._struct = struct; } static forName(codeOrigin, struct) { if (!this.mapping[struct]) this.mapping[struct] = new Sizeof(codeOrigin, struct); return this.mapping[struct]; } static resetMappings() { this.mapping = {}; } dump() { return "sizeof " + this.struct; } compare(other) { return this.struct.localeCompare(other.struct); } get struct() { return this._struct; } get isAddress() { return false; } get isLabel() { return false; } get isImmediate() { return true; } get isRegister() { return false; } } Sizeof.mapping = {}; class Immediate extends NoChildren { constructor(codeOrigin, value) { super(codeOrigin); if (value instanceof Number) value = value.valueOf(); this._value = value; if (typeof(value) != "number") throw "Bad immediate value " + value.inspect() + " at " + this.codeOriginString(); } dump() { return this.value.toString(); } equals(other) { return other instanceof Immediate && other.value == this.value; } get value() { return this._value; } get isAddress() { return false; } get isLabel() { return false; } get isImmediate() { return true; } get isImmediateOperand() { return true; } get isRegister() { return false; } } class AddImmediates extends Node { constructor(codeOrigin, left, right) { super(codeOrigin); this._left = left; this._right = right; } children() { return [this.left, this.right]; } dump() { return "(" + this.left.dump() + " + " + this.right.dump() + ")"; } value() { return (this.left.value() + this.right.value()).toString(); } get left() { return this._left; } get right() { return this._right; } get isAddress() { return false; } get isLabel() { return false; } get isImmediate() { return true; } get isImmediateOperand() { return true; } get isRegister() { return false; } } class SubImmediates extends Node { constructor(codeOrigin, left, right) { super(codeOrigin); this._left = left; this._right = right; } children() { return [this.left, this.right]; } dump() { return "(" + this.left.dump() + " - " + this.right.dump() + ")"; } value() { return (this.left.value() - this.right.value()).toString(); } get left() { return this._left; } get right() { return this._right; } get isAddress() { return false; } get isLabel() { return false; } get isImmediate() { return true; } get isImmediateOperand() { return true; } get isRegister() { return false; } } class MulImmediates extends Node { constructor(codeOrigin, left, right) { super(codeOrigin); this._left = left; this._right = right; } children() { return [this.left, this.right]; } dump() { return "(" + this.left.dump() + " * " + this.right.dump() + ")"; } get left() { return this._left; } get right() { return this._right; } get isAddress() { return false; } get isLabel() { return false; } get isImmediate() { return true; } get isImmediateOperand() { return false; } get isRegister() { return false; } } class NegImmediate extends Node { constructor(codeOrigin, child) { super(codeOrigin); this._child = child; } children() { return [this.child]; } dump() { return "(-" + this.child.dump() + ")"; } get child() { return this._child; } get isAddress() { return false; } get isLabel() { return false; } get isImmediate() { return true; } get isImmediateOperand() { return false; } get isRegister() { return false; } } class OrImmediates extends Node { constructor(codeOrigin, left, right) { super(codeOrigin); this._left = left; this._right = right; } children() { return [this.left, this.right]; } dump() { return "(" + this.left.dump() + " | " + this.right.dump() + ")"; } get left() { return this._left; } get right() { return this._right; } get isAddress() { return false; } get isLabel() { return false; } get isImmediate() { return true; } get isImmediateOperand() { return false; } get isRegister() { return false; } } class AndImmediates extends Node { constructor(codeOrigin, left, right) { super(codeOrigin); this._left = left; this._right = right; } children() { return [this.left, this.right]; } dump() { return "(" + this.left.dump() + " & " + this.right.dump() + ")"; } get left() { return this._left; } get right() { return this._right; } get isAddress() { return false; } get isLabel() { return false; } get isImmediate() { return true; } get isImmediateOperand() { return false; } get isRegister() { return false; } } class XorImmediates extends Node { constructor(codeOrigin, left, right) { super(codeOrigin); this._left = left; this._right = right; } children() { return [this.left, this.right]; } dump() { return "(" + this.left.dump() + " ^ " + this.right.dump() + ")"; } get left() { return this._left; } get right() { return this._right; } get isAddress() { return false; } get isLabel() { return false; } get isImmediate() { return true; } get isImmediateOperand() { return false; } get isRegister() { return false; } } class BitnotImmediate extends Node { constructor(codeOrigin, child) { super(codeOrigin); this._child = child; } children() { return [this.child]; } dump() { return "(~" + this.child.dump() + ")"; } get child() { return this._child; } get isAddress() { return false; } get isLabel() { return false; } get isImmediate() { return true; } get isImmediateOperand() { return false; } get isRegister() { return false; } } class StringLiteral extends NoChildren { constructor(codeOrigin, value) { super(codeOrigin); if (!value instanceof String || value[0] != "\"" || value.slice(-1) != "\"") throw "Bad string literal " + value.inspect() + " at " + this.codeOriginString(); this._value = value.slice(1, -1); } dump() { return "\"" + this.value + "\""; } equals(other) { return other instanceof StringLiteral && other.value == this.value; } get value() { return this._value; } get isAddress() { return false; } get isLabel() { return false; } get isImmediate() { return false; } get isImmediateOperand() { return false; } get isRegister() { return false; } } class RegisterID extends NoChildren { constructor(codeOrigin, name) { super(codeOrigin); this._name = name; } static forName(codeOrigin, name) { if (!this.mapping[name]) this.mapping[name] = new RegisterID(codeOrigin, name); return this.mapping[name]; } dump() { return this.name; } get name() { return this._name; } get isAddress() { return false; } get isLabel() { return false; } get isImmediate() { return false; } get isRegister() { return true; } } RegisterID.mapping = {}; class FPRegisterID extends NoChildren { constructor(codeOrigin, name) { super(codeOrigin); this._name = name; } static forName(codeOrigin, name) { if (!this.mapping[name]) this.mapping[name] = new FPRegisterID(codeOrigin, name); return this.mapping[name]; } dump() { return this.name; } get name() { return this._name; } get isAddress() { return false; } get isLabel() { return false; } get isImmediate() { return false; } get isImmediateOperand() { return false; } get isRegister() { return true; } } FPRegisterID.mapping = {}; class SpecialRegister extends NoChildren { constructor(name) { this._name = name; } get isAddress() { return false; } get isLabel() { return false; } get isImmediate() { return false; } get isImmediateOperand() { return false; } get isRegister() { return true; } } class Variable extends NoChildren { constructor(codeOrigin, name) { super(codeOrigin); this._name = name; } static forName(codeOrigin, name) { if (!this.mapping[name]) this.mapping[name] = new Variable(codeOrigin, name); return this.mapping[name]; } dump() { return this.name; } get name() { return this._name; } inspect() { return "