deep-c-rsc/JCGO/jtrsrc/com/ivmaisoft/jcgo/QualifiedName.java
2021-07-16 17:12:20 -05:00

598 lines
21 KiB
Java

/*
* @(#) $(JCGO)/jtrsrc/com/ivmaisoft/jcgo/QualifiedName.java --
* a part of JCGO translator.
**
* Project: JCGO (http://www.ivmaisoft.com/jcgo/)
* Copyright (C) 2001-2012 Ivan Maidanski <ivmai@mail.ru>
* All rights reserved.
*/
/*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
**
* This software is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License (GPL) for more details.
**
* Linking this library statically or dynamically with other modules is
* making a combined work based on this library. Thus, the terms and
* conditions of the GNU General Public License cover the whole
* combination.
**
* As a special exception, the copyright holders of this library give you
* permission to link this library with independent modules to produce an
* executable, regardless of the license terms of these independent
* modules, and to copy and distribute the resulting executable under
* terms of your choice, provided that you also meet, for each linked
* independent module, the terms and conditions of the license of that
* module. An independent module is a module which is not derived from
* or based on this library. If you modify this library, you may extend
* this exception to your version of the library, but you are not
* obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
package com.ivmaisoft.jcgo;
import java.util.Enumeration;
/**
* Grammar production for a qualified or simple name.
**
* Formats: ID Empty QualifiedName DOT ID
*/
final class QualifiedName extends LexNode {
private ClassDefinition callerClass;
private final ObjVector resultFields = new ObjVector();
private boolean lvalue;
private boolean setOnlyOrError;
private boolean forceCheck;
private boolean isFirstNotNull;
private boolean isClinitSafe;
private ExpressionType firstActualType;
QualifiedName(Term a) {
super(a, Empty.newTerm());
}
QualifiedName(Term a, Term c) {
super(a, c);
}
String dottedName() {
return terms[1].notEmpty() ? terms[0].dottedName() + "."
+ terms[1].dottedName() : terms[0].dottedName();
}
void storeDottedName(ObjVector v) {
terms[0].storeDottedName(v);
terms[1].storeDottedName(v);
}
boolean isName() {
return true;
}
boolean isJavaConstant(ClassDefinition ourClass) {
if (callerClass != null)
return resultFields.size() == 1
&& ((VariableDefinition) resultFields.elementAt(0))
.isJavaConstant();
ObjVector vec = new ObjVector();
storeDottedName(vec);
String name = (String) vec.elementAt(0);
ClassDefinition cd = ourClass;
assertCond(cd != null);
int lastInd = vec.size() - 1;
do {
VariableDefinition v = cd.getField(name, null);
if (v != null)
return lastInd == 0 && v.isJavaConstant();
cd = cd.outerClass();
} while (cd != null);
if (lastInd == 0)
return false;
Context c = ourClass.passOneContext();
ClassDefinition aclass = c.resolveClass(name, false, true);
int i = 1;
while (aclass == null) {
if (i >= lastInd)
return false;
name = name + "." + (String) vec.elementAt(i++);
aclass = c.resolveClass(name, false, false);
}
while (i < lastInd) {
name = name + "." + (String) vec.elementAt(i);
aclass = c.resolveClass(name, false, true);
if (aclass == null)
return false;
i++;
}
VariableDefinition v = aclass.getField((String) vec.elementAt(lastInd),
ourClass);
return v != null && v.isJavaConstant();
}
void processPass1(Context c) {
if (callerClass == null) {
callerClass = c.currentClass;
assertCond(callerClass != null);
lvalue = c.lvalue;
setOnlyOrError = c.setOnly;
ObjVector vec = new ObjVector();
storeDottedName(vec);
String name = (String) vec.elementAt(vec.size() - 1);
VariableDefinition v = null;
if (vec.size() > 1) {
ClassDefinition aclass = defineClass(c, vec);
if (aclass == null) {
fatalError(c, "Undefined qualified name: " + dottedName());
setUndefined();
return;
}
v = aclass.getField(name, c.forClass);
if (v == null) {
fatalError(c,
"Undefined qualified variable: " + aclass.name()
+ "." + name);
setUndefined();
return;
}
} else {
if (c.currentMethod != null) {
v = c.currentMethod.getLocalVar(name);
}
if (v == null) {
if ((c.forceVmExc & ClassDefinition.NULL_PTR_EXC) != 0) {
forceCheck = true;
}
ClassDefinition cd = callerClass;
while ((v = cd.getField(name, c.forClass)) == null) {
VariableDefinition outerV = cd.outerThisRef();
if (outerV != null) {
outerV.markUsed();
resultFields.addElement(outerV);
}
cd = cd.outerClass();
if (cd == null) {
fatalError(c, "Undefined variable: " + name);
setUndefined();
return;
}
}
if (!v.isClassVariable() && c.currentMethod != null
&& c.currentMethod.isClassMethod()) {
fatalError(c,
"Instance variable used in a static context: "
+ name);
}
}
}
v.markUsed(lvalue, c.setOnly);
resultFields.addElement(v);
if (lvalue) {
if (v.isLocalOrParam()) {
if (c.curTryChangedVars != null) {
c.curTryChangedVars.add(v);
}
} else {
v.setChangedSpecial();
}
}
processFields(c);
if (!v.used()
&& ((resultFields.size() > 1 && lvalue) || !isSafeExpr())) {
v.markUsed();
}
if (c.currentTry != null) {
c.currentTry.setVarAccessed((VariableDefinition) resultFields
.elementAt(0));
}
}
}
ClassDefinition defineClass(Context c, ObjVector vec) {
assertCond(vec != null);
boolean isMethodCall = false;
if (callerClass == null) {
callerClass = c.currentClass;
assertCond(callerClass != null);
isMethodCall = true;
}
int lastInd = vec.size() - 1;
if (lastInd == 0)
return null;
if ((c.forceVmExc & ClassDefinition.NULL_PTR_EXC) != 0) {
forceCheck = true;
}
int i = 1;
ClassDefinition aclass = null;
String name = (String) vec.elementAt(0);
VariableDefinition v = null;
if (c.currentMethod != null) {
v = c.currentMethod.getLocalVar(name);
}
if (v != null) {
v.markUsed();
aclass = v.exprType().receiverClass();
resultFields.addElement(v);
} else {
ClassDefinition cd = callerClass;
while ((v = cd.getField(name, c.forClass)) == null) {
VariableDefinition outerV = cd.outerThisRef();
if (outerV != null) {
resultFields.addElement(outerV);
}
cd = cd.outerClass();
if (cd == null)
break;
}
if (v != null && !v.isClassVariable()) {
if (c.currentMethod != null && c.currentMethod.isClassMethod()) {
fatalError(c,
"Instance variable used in a static context: "
+ name);
}
Enumeration en = resultFields.elements();
while (en.hasMoreElements()) {
((VariableDefinition) en.nextElement()).markUsed();
}
} else {
for (int j = resultFields.size() - 1; j >= 0; j--) {
resultFields.removeElementAt(j);
}
}
if (v != null) {
v.markUsed(false, false);
aclass = v.exprType().receiverClass();
resultFields.addElement(v);
} else {
aclass = c.resolveClass(name, false, true);
while (aclass == null) {
if (i >= lastInd)
return null;
name = name + "." + (String) vec.elementAt(i++);
aclass = c.resolveClass(name, false, false);
}
while (i < lastInd) {
name = name + "." + (String) vec.elementAt(i);
if ((cd = c.resolveClass(name, false, true)) == null)
break;
i++;
aclass = cd;
}
}
}
while (i < lastInd) {
v = aclass.getField((String) vec.elementAt(i++), c.forClass);
if (v == null) {
fatalError(c, "Undefined variable: " + dottedName());
break;
}
v.markUsed(false, false);
aclass = v.exprType().receiverClass();
resultFields.addElement(v);
}
if (isMethodCall && resultFields.size() > 0) {
processFields(c);
}
return aclass;
}
private void setUndefined() {
setOnlyOrError = true;
int i = resultFields.size();
while (i > 0) {
resultFields.removeElementAt(--i);
}
}
private void processFields(Context c) {
int i = resultFields.size();
while (i-- > 0) {
if (((VariableDefinition) resultFields.elementAt(i))
.isClassVariable())
break;
}
while (i > 0) {
resultFields.removeElementAt(--i);
}
VariableDefinition v = (VariableDefinition) resultFields.elementAt(0);
firstActualType = c.getActualType(v);
isFirstNotNull = c.isVarNotNull(v);
if (v.isClassVariable()) {
isClinitSafe = c.addAccessedClassField(v);
if (!isClinitSafe) {
v.markUsed();
}
} else if (!v.isLocalOrParam()) {
c.addAccessedClass(v.definingClass());
}
int count = resultFields.size();
if (count > 1) {
c.setVarNotNull(v);
}
i = 0;
while (++i < count) {
c.addAccessedClass(((VariableDefinition) resultFields.elementAt(i))
.definingClass());
}
}
ExpressionType exprType() {
assertCond(callerClass != null);
return resultFields.size() > 0 ? ((VariableDefinition) resultFields
.elementAt(resultFields.size() - 1)).exprType() : callerClass;
}
ExpressionType actualExprType() {
assertCond(callerClass != null);
int count = resultFields.size();
return count > 0 ? (count == 1 && firstActualType != null ? firstActualType
: ((VariableDefinition) resultFields.elementAt(count - 1))
.actualExprType())
: exprType();
}
ConstValue evaluateConstValue() {
assertCond(callerClass != null);
return resultFields.size() == 1 ? ((VariableDefinition) resultFields
.elementAt(0)).evaluateConstValue() : null;
}
boolean isLiteral() {
assertCond(callerClass != null);
return resultFields.size() == 1
&& ((VariableDefinition) resultFields.elementAt(0)).isLiteral();
}
boolean isImmutable() {
assertCond(callerClass != null);
int count = resultFields.size();
if (count > 0) {
VariableDefinition v = (VariableDefinition) resultFields
.elementAt(0);
if ((!isClinitSafe && v.isClassVariable() && v.definingClass()
.classInitializerNotCalledYet())
|| !v.isImmutable(callerClass))
return false;
for (int i = 1; i < count; i++) {
if (!((VariableDefinition) resultFields.elementAt(i))
.isImmutable(null))
return false;
}
}
return true;
}
boolean isSafeExpr() {
assertCond(callerClass != null);
int lastInd = resultFields.size() - 1;
if (lastInd >= 0) {
VariableDefinition v = (VariableDefinition) resultFields
.elementAt(0);
if (v.isClassVariable() && !isClinitSafe && !v.isLiteral()
&& v.definingClass().classInitializerNotCalledYet())
return false;
for (int i = lastInd - 1; i >= 0; i--) {
if ((i > 0 || !isFirstNotNull)
&& !((VariableDefinition) resultFields.elementAt(i))
.isNotNull())
return false;
}
}
return true;
}
boolean isSafeWithThrow() {
return true;
}
boolean isFieldAccessed(VariableDefinition v) {
assertCond(callerClass != null);
if (resultFields.size() == 0)
return false;
VariableDefinition field = (VariableDefinition) resultFields
.elementAt(0);
return v != null ? field == v
: resultFields.size() > 1
|| (!field.isLocalOrParam() && (!field
.isImmutable(callerClass) || (!isClinitSafe
&& field.isClassVariable() && field
.definingClass().classInitializerNotCalledYet())));
}
boolean isAnyLocalVarChanged(Term t) {
assertCond(callerClass != null);
if (!lvalue || resultFields.size() != 1)
return false;
VariableDefinition v = (VariableDefinition) resultFields.elementAt(0);
return v.isLocalOrParam() && (t == null || t.isFieldAccessed(v));
}
boolean isNotNull() {
assertCond(callerClass != null);
int count = resultFields.size();
return count == 0
|| (count == 1 && isFirstNotNull)
|| ((VariableDefinition) resultFields.elementAt(count - 1))
.isNotNull();
}
VariableDefinition getVariable(boolean allowInstance) {
assertCond(callerClass != null);
int count = resultFields.size();
return count == 0 ? VariableDefinition.THIS_VAR : allowInstance
|| count == 1 ? (VariableDefinition) resultFields
.elementAt(count - 1) : null;
}
String strLiteralValueGuess() {
assertCond(callerClass != null);
return resultFields.size() > 0 ? ((VariableDefinition) resultFields
.elementAt(resultFields.size() - 1)).strLiteralValueGuess()
: null;
}
ExpressionType classLiteralValGuess() {
assertCond(callerClass != null);
return resultFields.size() > 0 ? ((VariableDefinition) resultFields
.elementAt(resultFields.size() - 1)).classLiteralValGuess()
: null;
}
boolean storeClassLiteralsGuess(ObjVector parmSig, boolean isActual) {
assertCond(callerClass != null);
return resultFields.size() > 0
&& ((VariableDefinition) resultFields.elementAt(resultFields
.size() - 1))
.storeClassLiteralsGuess(parmSig, isActual);
}
MethodInvocation getClassNewInstanceCall() {
assertCond(callerClass != null);
return resultFields.size() > 0 ? ((VariableDefinition) resultFields
.elementAt(resultFields.size() - 1)).getClassNewInstanceCall()
: null;
}
MethodSignature getConstructorInstanceSign() {
assertCond(callerClass != null);
return resultFields.size() > 0 ? ((VariableDefinition) resultFields
.elementAt(resultFields.size() - 1))
.getConstructorInstanceSign() : null;
}
void discoverObjLeaks() {
if (resultFields.size() > 0) {
assertCond(callerClass != null);
((VariableDefinition) resultFields.elementAt(0))
.discoverLocalVolatile();
}
}
void setStackObjVolatile() {
assertCond(callerClass != null);
if (resultFields.size() == 1) {
((VariableDefinition) resultFields.elementAt(0))
.setStackObjVolatile();
} else if (resultFields.size() == 0) {
if (Main.dict.ourMethod != null) {
Main.dict.ourMethod.setThisStackObjVolatile();
} else {
callerClass.setInitThisStackObjVolatile();
}
}
}
void setObjLeaks(VariableDefinition v) {
assertCond(callerClass != null);
if (resultFields.size() == 1) {
VariableDefinition field = (VariableDefinition) resultFields
.elementAt(0);
if (field != v) {
if (field.isLocalOrParam()) {
ObjHashtable assignedLocals = Main.dict.assignedLocals;
assertCond(assignedLocals != null);
ObjVector assignedTerms = (ObjVector) assignedLocals
.get(field);
if (assignedTerms != null) {
int i = assignedTerms.size();
while (i-- > 0) {
Term t = (Term) assignedTerms.elementAt(i);
assignedTerms.removeElementAt(i);
t.setObjLeaks(v);
if (assignedTerms.identityLastIndexOf(t) < 0) {
assignedTerms.addElement(t);
}
}
}
} else {
field.setWritableArray(v);
}
}
} else if (resultFields.size() == 0
&& (v == null || !v.addSetObjLeaksTerm(this))) {
if (Main.dict.ourMethod != null) {
Main.dict.ourMethod
.setThisObjLeak(v == VariableDefinition.RETURN_VAR);
} else {
callerClass.setInstanceInitLeaks();
}
}
}
int tokenCount() {
int count = resultFields.size();
if (count == 0)
return 1;
VariableDefinition v = (VariableDefinition) resultFields.elementAt(0);
return (v.isClassVariable() || v.isLocalOrParam() || !v.used() ? 0 : 1)
+ count;
}
int tokensExpandedCount() {
return resultFields.size() == 1 ? ((VariableDefinition) resultFields
.elementAt(0)).tokensExpandedCount() : 0;
}
boolean isAtomary() {
return true;
}
void processOutput(OutputContext oc) {
oc.cPrint(stringOutput());
}
String stringOutput() {
assertCond(callerClass != null);
int count = resultFields.size();
if (count == 0)
return setOnlyOrError ? VariableDefinition.UNKNOWN_NAME
: This.CNAME;
VariableDefinition v = (VariableDefinition) resultFields.elementAt(0);
String resultString = v.isClassVariable() ? v.stringOutputForStatic(
isClinitSafe, lvalue && count == 1) : v.stringOutput(true,
lvalue && count == 1);
if (count > 1) {
boolean isPrevNotNull = isFirstNotNull || v.isNotNull();
int i = 1;
do {
v = (VariableDefinition) resultFields.elementAt(i);
if (++i >= count)
break;
resultString = v.stringOutput(resultString, isPrevNotNull ? 1
: forceCheck ? -1 : 0, false);
isPrevNotNull = v.isNotNull();
} while (true);
resultString = v.stringOutput(resultString, isPrevNotNull ? 1
: forceCheck ? -1 : 0, lvalue);
}
return resultString;
}
ExpressionType traceClassInit() {
assertCond(callerClass != null);
int lastInd = resultFields.size() - 1;
for (int i = 0; i < lastInd; i++) {
((VariableDefinition) resultFields.elementAt(i)).traceClassInit();
}
return setOnlyOrError ? null : lastInd < 0 ? Main.dict.curTraceInfo
.curThisClass() : ((VariableDefinition) resultFields
.elementAt(lastInd)).traceClassInit();
}
}