Skip to content
Snippets Groups Projects
Commit 00c5cd50 authored by Xun Yang's avatar Xun Yang
Browse files

Merge branch 'namenode' into 'master'

Namenode

See merge request !19
parents 85c36eaa 196c2792
No related branches found
No related tags found
2 merge requests!20Master,!19Namenode
......@@ -58,17 +58,30 @@ def reachabilityChecking(ASTs):
####################################################
# Preparation before code Gen:
# 1. Calculating the size of the object from each class
# 1. Calculating the size of the object from each class
# (also simultaneosly creating fieldOffset because fieldOffset table is required to generate size)
def codeGenPrep(ASTs):
interM = []
for t in ASTs:
classInterNode = t[1].typeDcl
if classInterNode and classInterNode.__class__.__name__ == "ClassNode":
classInterNode.populateSizeAndFieldOffset()
else: # interfaceNode, get their methods to prep for SIT
interM += classInterNode.methods
if interM: # prep SIT
for i in range(len(interM)):
interM[i].methodOffset = i * 4
interM[i].isInterM = True
for t in ASTs:
classInterNode = t[1].typeDcl
if classInterNode and classInterNode.__class__.__name__ == "ClassNode":
classInterNode.SITsize = len(interM)
def codeGen(ASTs, output="output"):
ASTs = ASTs[:1] # TOREMOVE: don't compile stdlib for now
codeGenPrep(ASTs)
ASTs = ASTs[:10] # TOREMOVE: don't compile stdlib for now
codeGenPrep(ASTs)
for t in ASTs:
t[1].codeGen()
# Outputting the generated code into className.s
......
......@@ -79,9 +79,14 @@ def genProcedure(content, comment):
# Adds method invoke prologue and eilogie around the content
def genMethodInvoke(method):
pro = ";Calling a method " + method + "\n" \
pro = ";Calling a method " + method + "\n" + \
p(instruction="push", arg1="ecx", comment="saving caller-save reg ecx") + \
p(instruction="push", arg1="edx", comment="saving caller-save reg edx")
epi = "; End of method invocation\n"
epi = p(instruction="pop", arg1="edx", comment="restoring calleer-save reg edx") + \
p(instruction="pop", arg1="ecx", comment="restoring caller-save reg ecx") + \
"; End of method invocation\n"
return (pro, epi)
......
......@@ -247,13 +247,18 @@ class AssignNode(ASTNode):
self.outMaybe = inMaybe
def codeGen(self):
self.code = ""
self.code +=("; Evaluate right of assignment\n")
self.right.codeGen()
self.code = self.right.code
self.code += p("push", "eax")
self.code +=("; Evaluate left of assignment\n")
self.code += self.left.addr()
if self.left.__class__.__name__ == "NameNode":
if self.left.prefixLink.__class__.__name__ == "VarDclNode":
# move init result to var location
self.code += p("mov", "[ebp - " + str(self.left.prefixLink.offset) + "]", "eax")
self.code += p("pop", "ebx")
self.code += p("mov", "[eax]", "ebx")
self.code += ("; End of assignment\n")
##################################################################################
......@@ -723,28 +728,15 @@ class FieldAccessNode(ASTNode):
# generates code that evaluates the address of field access
# result stored in eax (address)
# NOTE: this is always called by codeGen, so self.code is already intialized to ""
def addr(self):
result = ""
fieldNode = self.ID.prefixLink
if fieldNode.__class__.__name__ == "VarDclNode":
fieldNode = self.ID.staticField
if "static" in fieldNode.mods:
label = fieldNode.typeName + "_" + fieldNode.name
result += "; Start of calculating address for static field: " + label + "\n" + \
importHelper(fieldNode.typeName, self.typeName, "S_"+label) + \
p(instruction="mov", arg1="eax", arg2="dword S_"+label, comment="getting address to static field") + \
"; End of field access\n"
if not self.primary:
return self.ID.addr()
else:
result += "; Start of calculating address for non-static field\n"
if self.primary:
self.primary.codeGen()
result += self.primary.code
else:
self.ID.codeGen()
result += self.ID.code
self.primary.codeGen()
result += self.primary.code
# Null check
# At this point, eax stores the address of the object
result += p(instruction="call", arg1="H__Null_Check", comment="calling null check function")
......@@ -756,16 +748,23 @@ class FieldAccessNode(ASTNode):
def codeGen(self):
if hasattr(self, "code"):
return
if hasattr(self, "code"):
return
fieldNode = self.ID.prefixLink
label = fieldNode.typeName + "_" + fieldNode.name
self.code = "; Accessing a field :" + label + "\n"
# Evaluating the address of the field we're trying to access
if self.primary:
self.code += self.addr()
self.code += p(instruction="mov", arg1="eax", arg2="[eax]")
else:
self.ID.codeGen()
self.code += self.ID.code
self.code += "; End of field access\n"
fieldNode = self.ID.prefixLink
label = fieldNode.typeName + "_" + fieldNode.name
self.code = "; Accessing a field :" + label + "\n"
# Evaluating the address of the field we're trying to access
self.code += self.addr()
self.code += p(instruction="mov", arg1="eax", arg2="[eax]") + \
"; End of field access\n"
###################################################################################
......@@ -828,11 +827,6 @@ class MethodInvNode(ASTNode):
self.methodClass = self.primary.myType
if m:
# I don't see any need for this check, this check causes more harm than good
# because you can call System.out.println, where 'System' is a class with static field 'out',
# which is of type 'PrintStream' which has non-static method 'println', thus going into this if statement
# if self.ID.shouldBeStatic and (not 'static' in m.mods):
# raise Exception("ERROR: Static access of non-static method {}.".format(m.name))
# check static
if (not self.ID.shouldBeStatic) and 'static' in m.mods:
......@@ -854,47 +848,90 @@ class MethodInvNode(ASTNode):
raise Exception("ERROR: not reaching a variable declaration statement for var {}".format(self.name))
self.outMaybe = inMaybe
def evalArgs(self):
# Evaluate arguments
if self.args and hasattr(self.args, "codeGen"):
if not hasattr(self.args, "code"):
self.args.codeGen()
self.code += self.args.code
def codeGen(self):
if hasattr(self, "code"):
return
self.code = ""
# Only invoking static methods
# Static methods
if "static" in self.method.mods:
# 1. Prologue
mLabel = "M_" + self.method.typeName + "_" + self.method.name + "_" + self.method.paramTypes
(pro, epi) = genMethodInvoke(mLabel)
self.code += pro
# Evaluate arguments
if self.args and hasattr(self.args, "codeGen"):
if not hasattr(self.args, "code"):
self.args.codeGen()
self.code += self.args.code
# 2. Evaluating arguments
self.evalArgs()
# Calling static method
# 3. Calling static method
self.code += importHelper(self.method.typeName, self.typeName, mLabel)
self.code += p(instruction="call", arg1=mLabel, comment="calling method")
# Popping off all the arguments
# 4. Popping off all the arguments
toPop = 0
if self.args:
toPop = len(self.args.exprs)
toPop = len(self.args.exprs)*4
self.code += p(instruction="add", arg1="esp", arg2=toPop, comment="popping off arguments")
self.code += p(instruction="add", arg1="esp", arg2=toPop*4, comment="popping off all arguments off stack")
# Epilogue
# 5. Epilogue
self.code += epi
# TODO: To be replaced later
# Non static methods
else:
for c in self.children:
if c and hasattr(c, "codeGen"):
# children hasn't generated code yet
# Note: this check is redundant if we're certain that every override of this method has the initial check
if not hasattr(c, "code"):
c.codeGen()
self.code += c.code
# 1. Prologue (saving caller-save registers)
(pro, epi) = genMethodInvoke(self.method.name + "_" + self.method.paramTypes)
self.code += pro
# 2. Evaluate arguments and pushing them on the stack (handled by the args node)
self.evalArgs()
# 3. Evaluating o, the object
if self.primary:
if not hasattr(self.primary, "code"):
self.primary.codeGen()
self.code += self.primary.code
else: # a name node
if not hasattr(self.ID, "code"):
self.ID.codeGen()
self.code += self.ID.code
# 4. Null check
# At this point, eax stores the address of the object
self.code += p(instruction="call", arg1="H__Null_Check", comment="calling null check function")
# 5. Pushing the object pointer on the stack
self.code += p(instruction="push", arg1="eax", comment="pushing object pointer to the stack")
# 6. Fetch vtable
toPop = 0
if self.args:
toPop = len(self.args.exprs)*4
self.code += p(instruction="mov", arg1="eax", arg2="[eax]", comment="get vtable")
# 7. Fetching the address of m's implementation
mOffset = self.method.methodOffset
if self.methodClass.__class__.__name__ == "InterNode":
self.code += p(instruction="mov", arg1="eax", arg2="[eax]", comment="SIT column")
self.code += p(instruction="mov", arg1="eax", arg2="[eax+"+str(mOffset)+"]", comment="get address of method impl")
# 8. Calling the method
self.code += p(instruction="call", arg1="eax", comment="calling method")
# 9. Popping off arguemnts and object off stack
self.code += p(instruction="add", arg1="esp", arg2=toPop+4, comment="pop off args and object")
# 10. Epilogue
self.code += epi
# All children's code gen is already called
......
......@@ -105,7 +105,8 @@ class MethodNode(ASTNode):
self.typeName = typeName
self.order = order
self.myType = None
# self.SIToffset for methods that implements interface method
self.isInterM = False # if self is an interface method
self.replace = None # pointer to the method that this method overrides
# get method name
nameNodes = getParseTreeNodes(['ID'], parseTree, ['params', 'type', 'methodBody'])
......@@ -224,7 +225,7 @@ class MethodNode(ASTNode):
# params
for i, param in enumerate(self.params):
param.offset = i * 4 + 8
param.offset = i * 4 + 12 # 12 since the stack is now of the order: ebp, eip, o, params
if self.body:
......@@ -258,7 +259,7 @@ class MethodNode(ASTNode):
self.label = "M_" + self.typeName + "_" + self.name + "_" + self.paramTypes
self.code = pLabel(self.typeName + "_" + self.name + "_" + self.paramTypes, "method") # label
thisLoc = len(self.params) * 4 + 8
thisLoc = 8 # Right after ebp, eip
bodyCode = ""
if self.body:
......@@ -268,9 +269,9 @@ class MethodNode(ASTNode):
vars[i].offset = i * 4 + 16
bodyCode += p("push", 0)
# call parent constructor
# call parent constructor(zero argument)
if myClass.superClass:
suLabel = "M_" + myClass.superClass.name + "_"
suLabel = "M_" + myClass.superClass.name + "_" + myClass.superClass.name + "_"
bodyCode += importHelper(myClass.superClass.name, self.typeName, suLabel)
bodyCode += p("mov", "eax", "[ebp - " + str(thisLoc) + "]")
bodyCode += p("push", "eax", None, "# Pass THIS as argument to superClass.")
......@@ -301,6 +302,13 @@ class MethodNode(ASTNode):
self.code += genProcedure(bodyCode, "Constructor definition for " + self.name + " " + self.paramTypes)
# gets the top-most level method that this method overrides
def getTopReplace(self):
if not self.replace:
return self
else:
return self.replace.getTopReplace()
############# helper for forward ref checking ########
# Input: AST Node
# Output: A list of names to be check
......
......@@ -4,6 +4,7 @@ import MemberNodes
import TypeNodes
from Environment import Env
from CodeGenUtils import p
from codeGenNodes import genNameNode
# name nodes: contains compID and IDs
......@@ -37,7 +38,7 @@ class NameNode(ASTNode):
self.shouldBeStatic = False
self.pointToThis = False
self.staticField = None
self.prefixNodes = [] # stores the past prefix links so we can evaluate them one by one
self.name = getParseTreeNodes(["ID", "COMPID"], parseTree)[0].lex
self.IDs = self.name.split(".")
......@@ -66,6 +67,10 @@ class NameNode(ASTNode):
if self.IDs[0] == "this":
typeNode = self.env.getNode(self.typeName, "type")
self.addToPrefix(typeNode)
cNameNode = genNameNode(self.typeName)
cNameNode.isThis = True
self.prefixNodes.append(cNameNode)
return True
return False
......@@ -84,6 +89,10 @@ class NameNode(ASTNode):
localVarNode = self.env.findNode(ID, "expr")
if localVarNode:
self.addToPrefix(localVarNode)
cNameNode = genNameNode(self.typeName)
cNameNode.nodeLink = self.env.getNode(ID, "expr")
self.prefixNodes.append(cNameNode)
return True
return False
......@@ -99,6 +108,12 @@ class NameNode(ASTNode):
self.methodClass = self.env.getNode(self.typeName, 'type')
self.prefixLink = "contain"
self.addToPrefix("contain")
# evaluate THIS first, fieldnode will be added to prefixNodes in checkType()
cNameNode = genNameNode(self.typeName)
cNameNode.isThis = True
self.prefixNodes.append(cNameNode)
return True
return False
......@@ -143,6 +158,11 @@ class NameNode(ASTNode):
if "protected" in typeFieldNode.mods:
checkProtected(typeFieldNode, self)
cNameNode = genNameNode(self.typeName)
cNameNode.nodeLink = typeFieldNode
cNameNode.isStatic = True
self.prefixNodes.append(cNameNode)
return True
return False
......@@ -152,6 +172,9 @@ class NameNode(ASTNode):
self.prefixLink = typeNode
self.prefix = currPrefix
self.IDs = self.IDs[index+1:]
# If this is a static method, no codeGen needed at NameNode
return True
return False
......@@ -215,15 +238,25 @@ class NameNode(ASTNode):
else:
if curType.myType and curType.myType.isArray and self.IDs[0] == 'length':
self.myType = TypeStruct("int", None)
# TODO: handle length at nameNode
return
else:
if curType.__class__.__name__ in ['ParamNode', 'VarDclNode']:
curType = curType.myType.typePointer
curType = curType.env.getNode(self.IDs[0], 'fieldDcl')
cNameNode = genNameNode(self.typeName)
cNameNode.nodeLink = curType
self.prefixNodes.append(cNameNode)
# for methods, we want to keep prefixLink pointing to the class for getting method later
else:
curType = curType.env.getNode(self.IDs[0], 'fieldDcl')
cNameNode = genNameNode(self.typeName)
cNameNode.nodeLink = curType
self.prefixNodes.append(cNameNode)
# at this stage, all newly resolved field should be non static:
if curType.__class__.__name__ == 'FieldNode':
if 'static' in curType.mods:
......@@ -254,12 +287,21 @@ class NameNode(ASTNode):
# pprint(vars(self))
raise Exception("ERROR: Cannot check type of name {}".format(self.name))
# generates code that evaluates the address of whatever this NameNode is
# result stored in eax (address)
def addr(self):
result = ""
for c in self.prefixNodes[:-1]:
result += c.codeGen()
result += self.prefixNodes[-1].addr()
return result
def codeGen(self):
self.code = ""
if self.prefixLink.__class__.__name__ == "VarDclNode":
self.code = p("mov", "eax", "[ebp - " + str(self.prefixLink.offset) + "]", "access local var " + self.name)
if self.prefixLink.__class__.__name__ == "ParamNode":
self.code = p("mov", "eax", "[ebp + " + str(self.prefixLink.offset) + "]", "access param " + self.name)
for c in self.prefixNodes:
self.code += c.codeGen()
# helper
def checkProtected(dcl, usage):
......
// CODE_GENERATION
// expected result 42 + 2 + 42 + 5 = 91
import pkg.*;
public class Main {
public Main() {}
public int j = 5;
public static int test() {
Interface a = new pkg.Class1();
Object o = a.clone();
return o.hashCode() + a.k(2);
}
}
// CODE_GENERATION
package pkg;
public class Class1 implements Interface {
public Class1() {}
public Object clone() { return this; }
public int k(int i) {return i + 42;}
}
// CODE_GENERATION
package pkg;
public interface Interface {
public Object clone();
public int k(int i);
}
public class J1_01_compID_param {
public J1_01_compID_param() {
j = 5;
}
public int i = 4;
public int j = 1;
public static int n = 10;
public int m(J1_01_compID_param p){
p.i = p.i + 2;
return j + p.i;
}
public static int test() {
J1_01_compID_param k = new J1_01_compID_param();
return k.i + k.m(k) + J1_01_compID_param.n;
}
}
public class J1_01_fieldAcc_ObjCreate {
public J1_01_fieldAcc_ObjCreate() {
j = 5;
}
public int i = 4;
public int j = 1;
public static int n = 10;
public int m(){
return j;
}
public static int test() {
J1_01_ObjCreate k = new J1_01_fieldAcc_ObjCreate();
return k.i + k.m() + J1_01_fieldAcc_ObjCreate.n;
}
}
......@@ -16,6 +16,7 @@ class ClassInterNode(ASTNode):
self.env = None
self.children = []
self.canonName = ''
self.SITsize = 0
# sets
self.inherits = []
......@@ -45,6 +46,7 @@ class ClassInterNode(ASTNode):
unique.append(key)
contains = self.getContains([])
self.contains = contains
# 10. A class that contains any abstract methods must be abstract.
if isinstance(self, ClassNode):
......@@ -92,6 +94,8 @@ class ClassInterNode(ASTNode):
if 'abstract' not in scc.mods:
safeReplace(sic, scc, self.name)
sicOverwritten = True
if sc.__class__.__name__ == "MethodNode":
c.replace = sc
break
if not sicOverwritten:
superContains.append(sic)
......@@ -123,6 +127,8 @@ class ClassInterNode(ASTNode):
if type(sc) == type(c) and sc == c:
safeReplace(sc, c, self.name)
scOverwritten = True
if sc.__class__.__name__ == "MethodNode":
c.replace = sc
break
if not scOverwritten:
contains.append(sc)
......@@ -305,21 +311,27 @@ class ClassNode(ClassInterNode):
return
self.code = "" # For read-only section
self.code = "" # For read-only section
self.data = "" # For writeable data section
# print("This is the super class: {}".format(self.superClass))
if self.canonName == "java.lang.Object":
self.data += p("global", "I_SIT_" + self.name)
# Generate class label
self.label = pLabel(name=self.name, type="class")
self.data += ";START OF CLASS MEMORY LAYOUT FOR CLASS: " + self.canonName + "\n"
self.data += self.label
# SIT
self.data += pLabel("SIT_spot_"+ self.name, "inter") # type 'inter' for interface methods
self.data += p("dd", "42")
# TODO: SIT and subtype testing tables
# TODO: subtype testing tables
####### ADDING METHODS TO CLASS MEMORY LAYOUT AND FIELDS TO FIELD OFFSET TABLE #########
# Copying over the offsets of methods from superclass and DECLARING memory segment for the methods
lastMethodOffset = -4 # stores the largest method offset in the superCalss
lastMethodOffset = 0 # stores the largest method offset in the superCalss
# TODO: set this to 4 after the implemntation of both the SIT and subtype testing table
# Note: This is 4 less than the offset of where the next method would be located
# This is to accomodate for the addition of 4 to lastMethodOffset in EVERY (including the first) iteration in the
......@@ -340,7 +352,7 @@ class ClassNode(ClassInterNode):
for method in self.constructors:
lastMethodOffset += 4
self.methodOffset[(method.name, method.paramTypes)] = lastMethodOffset
method.offset = lastMethodOffset
method.methodOffset = lastMethodOffset
self.data += pLabel(name=self.name + "_" + method.name + "_" + method.paramTypes, type="vtable")
self.data += p(instruction="dd", arg1=64) # just declaring a memory segment with a random number
......@@ -355,15 +367,20 @@ class ClassNode(ClassInterNode):
self.methodOffset[(method.name, method.paramTypes)] = lastMethodOffset
self.data += pLabel(name=self.name + "_" + method.name + "_" + method.paramTypes, type="vtable")
self.data += p(instruction="dd", arg1=64) # just declaring a memory segment with a random number
# Adding inherited method to the methodDict
# Adding inherited method to the methodDict
for i in self.inherits:
if isinstance(i, MethodNode):
key = (i.name, i.paramTypes)
if not key in methodDict:
methodDict[(i.name, i.paramTypes)] = i
# print(self.methodOffset)
# Layout SIT
self.data += pLabel("SIT_" + self.name, "inter")
for i in range(self.SITsize):
self.data += pLabel("SIT_" + str(i), "inter")
self.data += p("dd", "42")
self.data += ";END OF CLASS MEMORY LAYOUT FOR CLASS " + self.name + "\n"
......@@ -385,9 +402,35 @@ class ClassNode(ClassInterNode):
self.code += p(instruction="extern", arg1=mLabel, comment="importing method implementation label")
self.code += p(instruction="mov", arg1="eax", arg2=vLabel, comment="Filling in class memory segment for method " + mLabel)
self.code += p(instruction="mov", arg1="[eax]", arg2="dword " + mLabel)
self.code += "; End of function for filling in methods in class memory layout\n"
# fill in SIT
self.code += "\n; Filling in SIT\n"
self.code += p("mov", "eax", "I_SIT_spot_" + self.name)
self.code += p("mov", "[eax]", "dword I_SIT_" + self.name)
for m in self.contains:
if m.__class__.__name__ == "MethodNode":
sm = m.getTopReplace()
if sm.typeName != m.typeName and sm.isInterM:
dlabel = "I_SIT_" + str(sm.methodOffset // 4)
className = m.typeName
imLabel = "M_" + className + "_" + m.name + "_" + m.paramTypes
if className != self.name:
self.code += p("extern", imLabel)
self.code += p("mov", "eax", dlabel)
self.code += p("mov", "[eax]", "dword " + imLabel)
self.code += "; End of fill in SIT.\n"
self.code += p(instruction="ret", arg1="")
self.code += "; End of function for filling in class memory layout\n"
# print(self.name)
# print(self.fieldOffset)
......@@ -395,22 +438,22 @@ class ClassNode(ClassInterNode):
###########################################################
# Generating a function that allocates and initializes all static fields
# Note: 1. saving and restoring ebx, a callee-save register
# Generating a function that allocates and initializes all static fields
# Note: 1. saving and restoring ebx, a callee-save register
# 2. static fields are intialized in the order of declaration within the class and has to be intialized
# before the test() method is being called
self.code += "; Function for filling allocating space and initializing static fields for class " + self.name + "\n"
self.code += pLabel(name=self.name + "_" + "staticFieldMemoryInit", type="helper")
self.code += p(instruction="push", arg1="ebx", comment="saving ebx")
self.code += p(instruction="push", arg1="ebx", comment="saving ebx")
for field in self.fields:
if not hasattr(field, "code"):
field.codeGen()
self.code += field.code
self.data += field.data
self.code += p(instruction="pop", arg1="ebx", comment="restoring ebx")
self.code += p(instruction="pop", arg1="ebx", comment="restoring ebx")
self.code += p(instruction="ret", arg1="")
self.code += "; End of function for filling allocating space and initializing static fields for class " + self.name + "\n"
# Generating a function that calls both the class memory init and static field init functions
self.code += "; Function that calls staticFieldMemoryInit and classMemoryInit \n"
self.code += pLabel(name=self.name + "_" + "classAndStaticFieldInit", type="helper")
......
from CodeGenUtils import p, importHelper
# since nameNode is a mess, a simple class for the purpose of codeGen nameNode
class genNameNode():
def __init__(self, typeName):
self.isThis = False
self.isStatic = False
# self.isLength = False
self.nodeLink = None
self.typeName = typeName # which type this node resides in
self.code = ""
# returns code
def codeGen(self):
if self.isThis:
self.code = p("mov", "eax", "[ebp + 8]")
elif self.nodeLink.__class__.__name__ == "VarDclNode":
self.code = p("mov", "eax", "[ebp - " + str(self.nodeLink.offset) + "]")
elif self.nodeLink.__class__.__name__ == "ParamNode":
self.code = p("mov", "eax", "[ebp + " + str(self.nodeLink.offset) + "]")
elif self.nodeLink.__class__.__name__ == "FieldNode":
if self.isStatic:
label = self.nodeLink.typeName + "_" + self.nodeLink.name
self.code += importHelper(self.nodeLink.typeName, self.typeName, "S_"+label) + \
p("mov", "eax", "dword S_"+label)
self.code += p("mov", "eax", "[eax]")
else:
# object will already be in eax
# Null check
self.code += p("call", "H__Null_Check")
self.code += p("add", "eax", self.nodeLink.offset)
self.code += p("mov", "eax", "[eax]")
return self.code
def addr(self):
result = ""
if self.isThis:
result = p("mov", "eax", "ebp")
result += p("add", "eax", "8")
elif self.nodeLink.__class__.__name__ == "VarDclNode":
result = p("mov", "eax", "ebp")
result += p("sub", "eax", str(self.nodeLink.offset))
elif self.nodeLink.__class__.__name__ == "ParamNode":
result = p("mov", "eax", "ebp")
result += p("add", "eax", str(self.nodeLink.offset))
elif self.nodeLink.__class__.__name__ == "FieldNode":
if self.isStatic:
label = self.nodeLink.typeName + "_" + self.nodeLink.name
self.code += importHelper(self.nodeLink.typeName, self.typeName, "S_"+label) + \
p("mov", "eax", "dword S_"+label)
else:
# object will already be in eax
result += p("call", "H__Null_Check")
result += p("add", "eax", self.nodeLink.offset)
return result
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment