|
44 | 44 | import static com.oracle.graal.python.PythonLanguage.GRAALVM_MICRO; |
45 | 45 | import static com.oracle.graal.python.PythonLanguage.GRAALVM_MINOR; |
46 | 46 | import static com.oracle.graal.python.PythonLanguage.J_GRAALPYTHON_ID; |
| 47 | +import static com.oracle.graal.python.PythonLanguage.MAGIC_NUMBER; |
| 48 | +import static com.oracle.graal.python.PythonLanguage.MAGIC_NUMBER_BYTES; |
47 | 49 | import static com.oracle.graal.python.PythonLanguage.RELEASE_LEVEL; |
48 | 50 | import static com.oracle.graal.python.PythonLanguage.RELEASE_LEVEL_FINAL; |
49 | 51 | import static com.oracle.graal.python.nodes.BuiltinNames.J_EXTEND; |
50 | 52 | import static com.oracle.graal.python.nodes.BuiltinNames.J___GRAALPYTHON__; |
| 53 | +import static com.oracle.graal.python.nodes.BuiltinNames.T_FORMAT; |
| 54 | +import static com.oracle.graal.python.nodes.BuiltinNames.T_MTIME; |
51 | 55 | import static com.oracle.graal.python.nodes.BuiltinNames.T_SHA3; |
| 56 | +import static com.oracle.graal.python.nodes.BuiltinNames.T_SIZE; |
| 57 | +import static com.oracle.graal.python.nodes.BuiltinNames.T__IMP; |
52 | 58 | import static com.oracle.graal.python.nodes.BuiltinNames.T___GRAALPYTHON__; |
53 | 59 | import static com.oracle.graal.python.nodes.BuiltinNames.T___MAIN__; |
54 | 60 | import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___NAME__; |
|
63 | 69 | import static com.oracle.graal.python.runtime.exception.PythonErrorType.ImportError; |
64 | 70 | import static com.oracle.graal.python.runtime.exception.PythonErrorType.SystemError; |
65 | 71 | import static com.oracle.graal.python.runtime.exception.PythonErrorType.TypeError; |
| 72 | +import static com.oracle.graal.python.util.PythonUtils.ARRAY_ACCESSOR_LE; |
66 | 73 | import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; |
67 | 74 | import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached; |
68 | 75 | import static com.oracle.graal.python.util.PythonUtils.tsLiteral; |
|
128 | 135 | import com.oracle.graal.python.builtins.objects.str.StringUtils; |
129 | 136 | import com.oracle.graal.python.builtins.objects.tuple.PTuple; |
130 | 137 | import com.oracle.graal.python.lib.OsEnvironGetNode; |
| 138 | +import com.oracle.graal.python.lib.PyNumberLongNode; |
131 | 139 | import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; |
132 | 140 | import com.oracle.graal.python.lib.PyObjectGetItem; |
| 141 | +import com.oracle.graal.python.lib.PyObjectStrAsTruffleStringNode; |
133 | 142 | import com.oracle.graal.python.nodes.ErrorMessages; |
134 | 143 | import com.oracle.graal.python.nodes.PConstructAndRaiseNode; |
135 | 144 | import com.oracle.graal.python.nodes.PRaiseNode; |
|
150 | 159 | import com.oracle.graal.python.nodes.object.GetClassNode; |
151 | 160 | import com.oracle.graal.python.nodes.object.GetOrCreateDictNode; |
152 | 161 | import com.oracle.graal.python.nodes.statement.AbstractImportNode; |
| 162 | +import com.oracle.graal.python.nodes.util.CannotCastException; |
| 163 | +import com.oracle.graal.python.nodes.util.CastToJavaLongLossyNode; |
| 164 | +import com.oracle.graal.python.nodes.util.CastToJavaStringNode; |
153 | 165 | import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; |
154 | 166 | import com.oracle.graal.python.nodes.util.ToNativePrimitiveStorageNode; |
155 | 167 | import com.oracle.graal.python.runtime.ExecutionContext; |
@@ -457,6 +469,102 @@ private static Object[] convertToObjectArray(TruffleString[] arr) { |
457 | 469 | return objectArr; |
458 | 470 | } |
459 | 471 |
|
| 472 | + @Builtin(name = "load_bytecode_file", minNumOfPositionalArgs = 3) |
| 473 | + @GenerateNodeFactory |
| 474 | + abstract static class LoadBytecodeFileNode extends PythonBuiltinNode { |
| 475 | + |
| 476 | + static final TruffleString T_CHECK_HASH_BASED_PYCS = tsLiteral("check_hash_based_pycs"); |
| 477 | + static final TruffleString T__BOOTSTRAP = tsLiteral("_bootstrap"); |
| 478 | + public static final TruffleString T__VERBOSE_MESSAGE = tsLiteral("_verbose_message"); |
| 479 | + public static final TruffleString MESSAGE = tsLiteral("'{} matches {}'"); |
| 480 | + |
| 481 | + @Specialization |
| 482 | + static Object doit(VirtualFrame frame, Object bytecodePath, Object sourcePath, Object statResult, |
| 483 | + @Bind Node inliningTarget, |
| 484 | + @Bind PythonContext context, |
| 485 | + @Cached("createFor($node)") BoundaryCallData boundaryCallData) { |
| 486 | + Object savedState = BoundaryCallContext.enter(frame, boundaryCallData); |
| 487 | + try { |
| 488 | + return doLoadBytecodeFile(bytecodePath, sourcePath, statResult, inliningTarget, context); |
| 489 | + } finally { |
| 490 | + BoundaryCallContext.exit(frame, boundaryCallData, savedState); |
| 491 | + } |
| 492 | + } |
| 493 | + |
| 494 | + @TruffleBoundary |
| 495 | + private static Object doLoadBytecodeFile(Object bytecodePath, Object sourcePath, Object statResult, Node inliningTarget, PythonContext context) { |
| 496 | + /* |
| 497 | + * This builtin is used to load a bytecode file (.pyc) in a way that we can trust that |
| 498 | + * it really comes from that file. It enables unloading serialized DSL bytecode from |
| 499 | + * memory, so that it can be reparsed later from the same file. It also provides the |
| 500 | + * cache key for CallTarget cache in multicontext mode. |
| 501 | + */ |
| 502 | + try { |
| 503 | + // get_data |
| 504 | + TruffleString strBytecodePath = PyObjectStrAsTruffleStringNode.executeUncached(bytecodePath); |
| 505 | + TruffleFile bytecodeFile = context.getEnv().getPublicTruffleFile(strBytecodePath.toJavaStringUncached()); |
| 506 | + byte[] bytes = bytecodeFile.readAllBytes(); |
| 507 | + // _classify_pyc |
| 508 | + if (bytes.length < 16 || !Arrays.equals(bytes, 0, 4, MAGIC_NUMBER_BYTES, 0, 4)) { |
| 509 | + return PNone.NONE; |
| 510 | + } |
| 511 | + int flags = ARRAY_ACCESSOR_LE.getInt(bytes, 4); |
| 512 | + if ((flags & ~0b11) != 0) { |
| 513 | + return PNone.NONE; |
| 514 | + } |
| 515 | + long cacheKey; |
| 516 | + boolean hashBased = (flags & 0b1) != 0; |
| 517 | + // Note that mtime-based validation is the default, hashing is opt-in |
| 518 | + if (hashBased) { |
| 519 | + boolean checkSource = (flags & 0b10) != 0; |
| 520 | + cacheKey = ARRAY_ACCESSOR_LE.getLong(bytes, 16); |
| 521 | + String checkHashBasedPycs = ""; |
| 522 | + try { |
| 523 | + checkHashBasedPycs = CastToJavaStringNode.getUncached().execute(context.lookupBuiltinModule(T__IMP).getAttribute(T_CHECK_HASH_BASED_PYCS)); |
| 524 | + } catch (CannotCastException e) { |
| 525 | + // ignore |
| 526 | + } |
| 527 | + if (!checkHashBasedPycs.equals("never") && (checkSource || checkHashBasedPycs.equals("always"))) { |
| 528 | + // get_data |
| 529 | + TruffleString strSourcePath = PyObjectStrAsTruffleStringNode.executeUncached(sourcePath); |
| 530 | + TruffleFile sourceFile = context.getEnv().getPublicTruffleFile(strSourcePath.toJavaStringUncached()); |
| 531 | + byte[] sourceBytes = sourceFile.readAllBytes(); |
| 532 | + long sourceHash = ARRAY_ACCESSOR_LE.getLong(ImpModuleBuiltins.SourceHashNode.hashSource(MAGIC_NUMBER, sourceBytes, sourceBytes.length), 0); |
| 533 | + // _validate_hash_pyc |
| 534 | + if (cacheKey != sourceHash) { |
| 535 | + return PNone.NONE; |
| 536 | + } |
| 537 | + } |
| 538 | + } else { |
| 539 | + // _validate_timestamp_pyc |
| 540 | + Object mTimeObj = PyNumberLongNode.executeUncached(PyObjectGetItem.executeUncached(statResult, T_MTIME)); |
| 541 | + long mTime = CastToJavaLongLossyNode.executeUncached(mTimeObj); |
| 542 | + if (Integer.toUnsignedLong(ARRAY_ACCESSOR_LE.getInt(bytes, 8)) != mTime) { |
| 543 | + return PNone.NONE; |
| 544 | + } |
| 545 | + Object sizeObj = PyObjectGetItem.executeUncached(statResult, T_SIZE); |
| 546 | + if (sizeObj != PNone.NONE) { |
| 547 | + long size = CastToJavaLongLossyNode.executeUncached(sizeObj); |
| 548 | + if (Integer.toUnsignedLong(ARRAY_ACCESSOR_LE.getInt(bytes, 12)) != size) { |
| 549 | + return PNone.NONE; |
| 550 | + } |
| 551 | + } |
| 552 | + cacheKey = ARRAY_ACCESSOR_LE.getLong(bytes, 8); |
| 553 | + } |
| 554 | + if (context.getOption(PythonOptions.VerboseFlag)) { |
| 555 | + Object message = PyObjectCallMethodObjArgs.executeUncached(MESSAGE, T_FORMAT, bytecodePath, sourcePath); |
| 556 | + CallNode.executeUncached(context.lookupBuiltinModule(T__BOOTSTRAP).getAttribute(T__VERBOSE_MESSAGE), message); |
| 557 | + } |
| 558 | + return MarshalModuleBuiltins.fromBytecodeFile(context, bytecodeFile, bytes, 16, bytes.length - 16, cacheKey); |
| 559 | + } catch (MarshalModuleBuiltins.Marshal.MarshalError me) { |
| 560 | + throw PRaiseNode.raiseStatic(inliningTarget, me.type, me.message, me.arguments); |
| 561 | + } catch (IOException | SecurityException | UnsupportedOperationException | IllegalArgumentException e) { |
| 562 | + LOGGER.fine(() -> PythonUtils.formatJString("Failed to load bytecode file using load_bytecode_file: %s", e)); |
| 563 | + return PNone.NONE; |
| 564 | + } |
| 565 | + } |
| 566 | + } |
| 567 | + |
460 | 568 | @Builtin(name = "read_file", minNumOfPositionalArgs = 1) |
461 | 569 | @GenerateNodeFactory |
462 | 570 | public abstract static class ReadFileNode extends PythonUnaryBuiltinNode { |
|
0 commit comments