11
11
12
12
package kr .ac .kaist .safe .analyzer
13
13
14
+ import kr .ac .kaist .safe .{ SafeConfig , CmdCFGBuild }
14
15
import kr .ac .kaist .safe .errors .ExcLog
15
16
import kr .ac .kaist .safe .errors .error ._
16
17
import kr .ac .kaist .safe .analyzer .domain ._
@@ -19,9 +20,12 @@ import kr.ac.kaist.safe.analyzer.models.builtin._
19
20
import kr .ac .kaist .safe .nodes .ir ._
20
21
import kr .ac .kaist .safe .nodes .cfg ._
21
22
import kr .ac .kaist .safe .util ._
23
+ import kr .ac .kaist .safe .parser .Parser
24
+ import kr .ac .kaist .safe .phase ._
22
25
23
26
import scala .collection .immutable .{ HashMap , HashSet }
24
27
import scala .collection .mutable .{ HashMap => MHashMap , Map => MMap }
28
+ import scala .util .{ Success , Failure }
25
29
26
30
class Semantics (
27
31
cfg : CFG ,
@@ -178,7 +182,11 @@ class Semantics(
178
182
})
179
183
(newSt, AbsState .Bot )
180
184
}
181
- case call : Call => CI (cp, call.callInst, st, AbsState .Bot )
185
+ case call : Call => call.callInst match {
186
+ // for Node.js
187
+ case (_ : CFGLoadModule ) => loadModule(cp, call.callInst, st, AbsState .Bot )
188
+ case _ => CI (cp, call.callInst, st, AbsState .Bot )
189
+ }
182
190
case block : NormalBlock =>
183
191
block.getInsts.foldRight((st, AbsState .Bot ))((inst, states) => {
184
192
val (oldSt, oldExcSt) = states
@@ -1059,6 +1067,103 @@ class Semantics(
1059
1067
(AbsState .Bot , AbsState .Bot )
1060
1068
}
1061
1069
1070
+ // for Node.js
1071
+ // construct a CFG for a dynamically loaded module
1072
+ // @loadModule(thisArg, [path])
1073
+ def loadModule (cp : ControlPoint , i : CFGCallInst , st : AbsState , excSt : AbsState ): (AbsState , AbsState ) = {
1074
+ val loc = Loc (i.asite)
1075
+ val st1 = st.oldify(loc)
1076
+ val (thisVal, _) = V (i.thisArg, st1)
1077
+ val (argVal, _) = V (i.arguments, st1)
1078
+ val firstArgVal = argVal.locset.getSingle match {
1079
+ case ConOne (pathloc) =>
1080
+ (st1.heap.get(pathloc)(" 0" )).value
1081
+ case _ =>
1082
+ throw new Error (" The second arguement for @loadModule is not a single array " )
1083
+ }
1084
+
1085
+ // concrete path for the loaded source
1086
+ val path : String = firstArgVal.pvalue.strval.gamma match {
1087
+ // for now, we assume that a given path for the loaded module has a single concrete string
1088
+ case ConFin (strset) if strset.size == 1 => strset.head
1089
+ case ConFin (strset) if strset.size != 1 =>
1090
+ throw new Error (" Possible paths for loading the module are multiple : " + strset)
1091
+ case ConInf () => throw new Error (" Unknown path for the module to be loaded in Node.js" )
1092
+ }
1093
+ if (thisVal.isBottom)
1094
+ throw new Error (" thisArg in @loadModule(thisArg, [path]) is the bottom value." )
1095
+ else {
1096
+ // construct an AST
1097
+ val ast = Parser .moduleToAST(path) match {
1098
+ case Success ((program, excLog)) => {
1099
+ // Report errors.
1100
+ if (excLog.hasError) {
1101
+ println(program.relFileName + " :" )
1102
+ println(excLog)
1103
+ }
1104
+ program
1105
+ }
1106
+ // for now, throw an exception when the parsing failed
1107
+ case Failure (e) =>
1108
+ throw ModelParseError (e.toString)
1109
+ }
1110
+ // rewrite AST
1111
+ val safeConfig = SafeConfig (CmdCFGBuild , silent = true )
1112
+ val astRewriteConfig = ASTRewriteConfig ()
1113
+ val rast = ASTRewrite (ast, safeConfig, astRewriteConfig).get
1114
+
1115
+ // construct an IR
1116
+ val compileConfig = CompileConfig ()
1117
+ val ir = Compile (rast, safeConfig, compileConfig).get
1118
+
1119
+ // cfg build
1120
+ val cfgBuildConfig = CFGBuildConfig ()
1121
+ val funCFG = CFGBuild (ir, safeConfig, cfgBuildConfig).get
1122
+ val func = funCFG.getFunc(1 ).get
1123
+
1124
+ // add the cfg for the function to the current cfg
1125
+ cfg.addFunction(func)
1126
+
1127
+ // draw call/return edges
1128
+ val oldLocalEnv = st1.context.pureLocal
1129
+ val tp = cp.tracePartition
1130
+ val nCall = i.block
1131
+ val cpAfterCall = ControlPoint (nCall.afterCall, tp)
1132
+ val cpAfterCatch = ControlPoint (nCall.afterCatch, tp)
1133
+ // note that we directly get scope environment from the caller without the function object of the callee.
1134
+ val scopeValue = oldLocalEnv.outer
1135
+ val newEnv = AbsLexEnv .newPureLocal(AbsLoc (loc))
1136
+ val newRec = newEnv.record.decEnvRec
1137
+ // no arguments
1138
+ // .CreateMutableBinding(funCFG.argumentsName)
1139
+ // .SetMutableBinding(funCFG.argumentsName, argVal)
1140
+ val (newRec2, _) = newRec
1141
+ .CreateMutableBinding (" @scope" )
1142
+ .SetMutableBinding (" @scope" , scopeValue)
1143
+ val entryCP = cp.next(func.entry, CFGEdgeCall )
1144
+ val newTP = entryCP.tracePartition
1145
+ val exitCP = ControlPoint (func.exit, newTP)
1146
+ val exitExcCP = ControlPoint (func.exitExc, newTP)
1147
+ addIPEdge(cp, entryCP, EdgeData (
1148
+ OldASiteSet .Empty ,
1149
+ newEnv.copyWith(record = newRec2),
1150
+ thisVal
1151
+ ))
1152
+ addIPEdge(exitCP, cpAfterCall, EdgeData (
1153
+ st1.context.old,
1154
+ oldLocalEnv,
1155
+ st1.context.thisBinding
1156
+ ))
1157
+ addIPEdge(exitExcCP, cpAfterCatch, EdgeData (
1158
+ st1.context.old,
1159
+ oldLocalEnv,
1160
+ st1.context.thisBinding
1161
+ ))
1162
+ // TODO: exception handling
1163
+ (st1, excSt)
1164
+ }
1165
+ }
1166
+
1062
1167
def CI (cp : ControlPoint , i : CFGCallInst , st : AbsState , excSt : AbsState ): (AbsState , AbsState ) = {
1063
1168
// cons, thisArg and arguments must not be bottom
1064
1169
val loc = Loc (i.asite)
0 commit comments